Skip to content

Instantly share code, notes, and snippets.

@Aloxaf
Created July 4, 2020 02:20
Show Gist options
  • Select an option

  • Save Aloxaf/86d0fc9ba1d356b6210d9e95b7ddb6c5 to your computer and use it in GitHub Desktop.

Select an option

Save Aloxaf/86d0fc9ba1d356b6210d9e95b7ddb6c5 to your computer and use it in GitHub Desktop.

Revisions

  1. Aloxaf created this gist Jul 4, 2020.
    1,680 changes: 1,680 additions & 0 deletions fcitx5-diagnose.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,1680 @@
    #!/usr/bin/env bash

    shopt -s extglob nullglob globstar
    export TEXTDOMAIN=fcitx5

    __test_bash_unicode() {
    local magic_str='${1}'$'\xe4'$'\xb8'$'\x80'
    local magic_replace=${magic_str//\$\{/$'\n'$\{}
    ! [ "${magic_str}" = "${magic_replace}" ]
    }

    if type gettext &> /dev/null && __test_bash_unicode; then
    _() {
    gettext "$@"
    }
    else
    _() {
    echo "$@"
    }
    fi

    #############################
    # utility
    #############################

    __get_pretty_name() {
    local _home=$(realpath ~ 2> /dev/null || echo ~)
    local _orig=$(realpath "${1}" 2> /dev/null || echo "${1}")
    if [[ ${_orig}/ =~ ^${_home}/ ]]; then
    echo "~${_orig#${_home}}"
    else
    echo "${_orig}"
    fi
    }

    __conf_dir_init() {
    # Don't do any fancy check here, it's the user's fault, which we should detect
    # later, if it is set to some non-sense value.
    if [[ -n ${XDG_CONFIG_HOME} ]]; then
    _xdg_conf_home=${XDG_CONFIG_HOME}
    else
    _xdg_conf_home=~/.config
    fi
    fx_conf_home=${_xdg_conf_home}/fcitx5
    xdg_conf_pretty_name=$(__get_pretty_name "${_xdg_conf_home}")
    fx_conf_pretty_name=$(__get_pretty_name "${fx_conf_home}")
    }
    __conf_dir_init

    array_push() {
    eval "${1}"'=("${'"${1}"'[@]}" "${@:2}")'
    }

    _find_file() {
    local "${1}"
    eval "${2}"'=()'
    while IFS= read -r -d '' "${1}"; do
    array_push "${2}" "${!1}"
    done < <(find "${@:3}" -print0 2> /dev/null)
    }

    find_file() {
    # Avoid variable name conflict (Not that anyone is using the internal
    # variable name though...)
    if [[ ${1} = __find_file_line ]]; then
    _find_file __find_file_line2 "$@"
    else
    _find_file __find_file_line "$@"
    fi
    }

    str_match_glob() {
    local pattern=$1
    local str=$2
    case "$2" in
    $pattern)
    return 0
    ;;
    esac
    return 1
    }

    str_match_regex() {
    local pattern=$1
    local str=$2
    [[ $str =~ $pattern ]]
    }

    add_and_check_file() {
    local prefix="$1"
    local file="$2"
    local inode
    inode="$(stat -L --printf='%i' "${file}" 2> /dev/null)" || return 0
    local varname="___add_and_check_file_${prefix}_${inode}"
    [ ! -z "${!varname}" ] && return 1
    eval "${varname}=1"
    return 0
    }

    unique_file_array() {
    for f in "${@:3}"; do
    add_and_check_file "${1}" "${f}" && {
    array_push "${2}" "${f}"
    }
    done
    }

    print_array() {
    for ele in "$@"; do
    echo "${ele}"
    done
    }

    repeat_str() {
    local i
    local n="$1"
    local str="$2"
    local res=""
    for ((i = 0;i < n;i++)); do
    res="${res}${str}"
    done
    echo "${res}"
    }

    # require `shopt -s nullglob` and the argument needs to be a glob
    find_in_path() {
    local w="$1"
    local IFS=':'
    local p
    local f
    local fs
    for p in ${PATH}; do
    eval 'fs=("${p}/"'"${w}"')'
    for f in "${fs[@]}"; do
    echo "$f"
    done
    done
    }

    run_grep_fcitx() {
    "$@" | grep fcitx
    }

    get_config_dir() {
    local conf_option="$1"
    local default_name="$2"
    local path
    for path in "$(fcitx4-config "--${conf_option}" 2> /dev/null)" \
    "/usr/share/fcitx5/${default_name}" \
    "/usr/local/share/fcitx5/${default_name}"; do
    [ ! -z "${path}" ] && [ -d "${path}" ] && {
    echo "${path}"
    return 0
    }
    done 2> /dev/null
    return 1
    }

    get_from_config_file() {
    local file="$1"
    local key="$2"
    local value
    value=$(sed -ne "s=^${key}\=\(.*\)=\1=gp" "$file" 2> /dev/null)
    [ -z "$value" ] && return 1
    echo "${value}"
    return 0
    }

    get_locale() {
    local name=$1
    str_match_glob 'LC_*' "$name" || str_match_glob 'LANG' "$name" || {
    name="LC_$name"
    }
    [ -z "${LC_ALL}" ] || {
    echo "${LC_ALL}"
    return
    }
    [ -z "${!name}" ] || {
    echo "${!name}"
    return
    }
    [ -z "${LANG}" ] || {
    echo "${LANG}"
    return
    }
    echo "POSIX"
    }

    if type dbus-send &> /dev/null; then
    dbus_exe=dbus-send
    dbus_get_name_owner() {
    local address
    address=$(dbus-send --print-reply=literal --dest=org.freedesktop.DBus \
    /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner \
    "string:$1" 2> /dev/null) || return 1
    echo -n "${address##* }"
    }
    dbus_get_pid() {
    local pid
    pid=$(dbus-send --print-reply=literal --dest=org.freedesktop.DBus \
    /org/freedesktop/DBus org.freedesktop.DBus.GetConnectionUnixProcessID \
    "string:$1" 2> /dev/null) || return 1
    echo -n "${pid##* }"
    }
    elif qdbus_exe=$(which qdbus 2> /dev/null) || \
    qdbus_exe=$(which qdbus-qt4 2> /dev/null) || \
    qdbus_exe=$(which qdbus-qt5 2> /dev/null); then
    dbus_exe=${qdbus_exe}
    dbus_get_name_owner() {
    "${qdbus_exe}" org.freedesktop.DBus /org/freedesktop/DBus \
    org.freedesktop.DBus.GetNameOwner "$1" 2> /dev/null
    }
    dbus_get_pid() {
    "${qdbus_exe}" org.freedesktop.DBus /org/freedesktop/DBus \
    org.freedesktop.DBus.GetConnectionUnixProcessID "$1" 2> /dev/null
    }
    else
    dbus_exe=
    dbus_get_name_owner() {
    return 1
    }
    dbus_get_pid() {
    return 1
    }
    fi

    print_process_info() {
    ps -o pid=,args= --pid "$1" 2> /dev/null && return
    cmdline=''
    [[ -d /proc/$1 ]] && {
    cmdline=$(cat /proc/$1/cmdline) || cmdline=$(cat /proc/$1/comm) || \
    cmdline=$(readlink /proc/$1/exe)
    } 2> /dev/null
    echo "$1 ${cmdline}"
    }

    # Detect DE

    _detectDE_XDG_CURRENT() {
    case "${XDG_CURRENT_DESKTOP}" in
    GNOME)
    DE=gnome
    ;;
    KDE)
    DE=kde
    ;;
    LXDE)
    DE=lxde
    ;;
    XFCE)
    DE=xfce
    ;;
    *)
    return 1
    ;;
    esac
    }

    _detectDE_classic() {
    if [ x"$KDE_FULL_SESSION" = x"true" ]; then
    DE=kde
    elif xprop -root KDE_FULL_SESSION 2> /dev/null | \
    grep ' = \"true\"$' > /dev/null 2>&1; then
    DE=kde
    elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then
    DE=gnome
    elif [ x"$MATE_DESKTOP_SESSION_ID" != x"" ]; then
    DE=mate
    elif dbus_get_name_owner org.gnome.SessionManager > /dev/null; then
    DE=gnome
    elif xprop -root _DT_SAVE_MODE 2> /dev/null | \
    grep ' = \"xfce4\"$' >/dev/null 2>&1; then
    DE=xfce
    elif xprop -root 2> /dev/null | \
    grep -i '^xfce_desktop_window' >/dev/null 2>&1; then
    DE=xfce
    else
    return 1
    fi
    }

    _detectDE_SESSION() {
    case "$DESKTOP_SESSION" in
    gnome)
    DE=gnome
    ;;
    LXDE|Lubuntu)
    DE=lxde
    ;;
    xfce|xfce4|'Xfce Session')
    DE=xfce
    ;;
    *)
    return 1
    ;;
    esac
    }

    _detectDE_uname() {
    case "$(uname 2>/dev/null)" in
    Darwin)
    DE=darwin
    ;;
    *)
    return 1
    ;;
    esac
    }

    detectDE() {
    # see https://bugs.freedesktop.org/show_bug.cgi?id=34164
    unset GREP_OPTIONS

    _detectDE_XDG_CURRENT || _detectDE_classic || \
    _detectDE_SESSION || _detectDE_uname || {
    DE=generic
    }
    if [ x"$DE" = x"gnome" ]; then
    # gnome-default-applications-properties is only available in GNOME 2.x
    # but not in GNOME 3.x
    which gnome-default-applications-properties > /dev/null 2>&1 || \
    DE="gnome3"
    which gnome-shell &> /dev/null && DE="gnome3"
    fi
    }

    maybe_gnome3() {
    [[ $DE = gnome3 ]] && return 0
    [[ $DE = generic ]] && which gnome-shell &> /dev/null && return 0
    return 1
    }

    detectDE

    # user and uid

    detect_user() {
    if which id &> /dev/null; then
    cur_user=$(id -un)
    cur_uid=$(id -u)
    else
    if [[ -n $UID ]]; then
    cur_uid=$UID
    elif [[ -d /proc/$$/ ]]; then
    cur_uid=$(stat -c %u /proc/$$/)
    else
    cur_uid=""
    fi
    if which whoami &> /dev/null; then
    cur_user=$(whoami)
    elif [[ -d /proc/$$/ ]]; then
    cur_user=$(stat -c %U /proc/$$/)
    elif [[ -n $USER ]]; then
    cur_user=$USER
    else
    cur_user=""
    fi
    fi
    }

    detect_user &> /dev/null

    try_open() {
    (exec < "$1") &> /dev/null
    }

    _check_open_root() {
    for f in /proc/1/environ /proc/1/mem /proc/kcore /proc/kmem; do
    try_open "$f" && return 0
    done
    if which readlink &> /dev/null; then
    for f in /proc/1/exe /proc/1/cwd /proc/1/root; do
    readlink "$f" &> /dev/null && return 0
    done
    fi
    return 1
    }

    check_is_root() {
    if [[ $cur_uid = 0 ]]; then
    return 0
    elif [[ $cur_user = root ]]; then
    return 0
    elif [[ -n $cur_uid ]] && [[ -n $cur_user ]]; then
    return 1
    elif _check_open_root; then
    return 0
    fi
    return 1
    }

    # ldd

    run_ldd() {
    [ -f "$1" ] || return 1
    local __line
    local soname
    while read __line; do
    [[ $__line =~ ^[\ $'\t']*([^\ $'\t'].*) ]] || continue
    __line="${BASH_REMATCH[1]}"
    [[ $__line =~ ^([-_.a-zA-Z0-9]+)[\ $'\t']([^\ $'\t'].*) ]] || \
    continue
    soname="${BASH_REMATCH[1]}"
    __line="${BASH_REMATCH[2]}"
    [[ $__line =~ not[\ $'\t']+found ]] && echo "$soname"
    done <<< "$(LANG=C LC_ALL=C ldd "$1" 2> /dev/null)"
    }

    #############################
    # print
    #############################

    # tty and color
    __istty=0
    __use_color=0

    check_istty() {
    [ -t 1 ] && {
    __istty=1
    } || {
    __istty=0
    }
    case "${_use_color}" in
    true)
    __use_color=1
    ;;
    false)
    __use_color=0
    ;;
    auto)
    __use_color=$__istty
    ;;
    esac
    }

    print_tty_ctrl() {
    ((__use_color)) || return
    echo -ne '\e['"${1}"'m'
    }

    replace_reset() {
    local line
    local IFS=$'\n'
    if [ ! -z "$1" ]; then
    while read line; do
    echo "${line//$'\e'[0m/$'\e'[${1}m}"
    done
    [ -z "${line}" ] || {
    echo -n "${line//$'\e'[0m/$'\e'[${1}m}"
    }
    else
    cat
    fi
    }

    __replace_line() {
    local IFS=$'\n'
    local __line=${1//\$\{/$'\n'$\{}
    shift
    local __varname
    echo "${__line}" | while read __line; do
    if [[ ${__line} =~ ^\$\{([_a-zA-Z0-9]+)\} ]]; then
    __varname="${BASH_REMATCH[1]}"
    echo -n "${__line/\$\{${__varname}\}/${!__varname}}"
    else
    echo -n "${__line}"
    fi
    done
    echo
    }

    __replace_vars() {
    local IFS=$'\n'
    local __line
    while read __line; do
    __replace_line "${__line}" "$@"
    done
    [ -z "${__line}" ] || {
    echo -n "$(__replace_line "${__line}" "$@")"
    }
    }

    print_eval() {
    echo "$1" | __replace_vars "${@:2}"
    }

    # print inline
    code_inline() {
    print_tty_ctrl '01;36'
    echo -n '`'"$1"'`' | replace_reset '01;36'
    print_tty_ctrl '0'
    }

    print_link() {
    local text="$1"
    local url="$2"
    print_tty_ctrl '01;33'
    echo -n "[$text]($url)" | replace_reset '01;33'
    print_tty_ctrl '0'
    }

    escape_url_get() {
    local get="$1"
    echo -n "$get" | sed -e 's/&/%26/g' -e 's/+/%2B/g' -e 's/ /+/g'
    }

    print_google_link() {
    local text="$1"
    local url="https://www.google.com/search?q=$(escape_url_get "${text}")"
    print_link "${text}" "${url}"
    }

    print_not_found() {
    print_eval "$(_ '${1} not found.')" "$(code_inline $1)"
    }

    # indent levels and list index counters
    __current_level=0
    __list_indexes=(0)

    set_cur_level() {
    local level="$1"
    local indexes=()
    local i
    if ((level >= 0)); then
    ((__current_level = level))
    for ((i = 0;i <= __current_level;i++)); do
    ((indexes[i] = __list_indexes[i]))
    done
    __list_indexes=("${indexes[@]}")
    else
    ((__current_level = 0))
    __list_indexes=()
    fi
    }

    increase_cur_level() {
    local level="$1"
    ((level = __current_level + level))
    set_cur_level "$level"
    }

    # print blocks
    __need_blank_line=0

    write_paragraph() {
    local str="$1"
    local p1="$2"
    local p2="$3"
    local code="$4"
    local prefix="$(repeat_str "${__current_level}" " ")"
    local line
    local i=0
    local whole_prefix
    local IFS=$'\n'
    ((__need_blank_line)) && echo
    {
    while read line; do
    [ -z "${code}" ] || print_tty_ctrl "${code}"
    {
    ((i == 0)) && {
    whole_prefix="${prefix}${p1}"
    } || {
    whole_prefix="${prefix}${p2}"
    }
    ((i++))
    [ -z "${line}" ] && {
    echo
    } || {
    echo "${whole_prefix}${line}"
    }
    } | replace_reset "${code}"
    [ -z "${code}" ] || print_tty_ctrl "0"
    done
    } <<< "${str}"
    __need_blank_line=1
    }

    write_eval() {
    write_paragraph "$(print_eval "$@")"
    }

    write_error() {
    write_paragraph "**${1}**" "${2}" "${3}" '01;31'
    }

    write_error_eval() {
    write_error "$(print_eval "$@")"
    }

    write_quote_str() {
    local str="$1"
    increase_cur_level 1
    __need_blank_line=0
    echo
    write_paragraph "${str}" '' '' '01;35'
    echo
    __need_blank_line=0
    increase_cur_level -1
    }

    write_quote_cmd() {
    local cmd_output_str cmd_ret_val
    cmd_output_str="$("$@" 2>&1)"
    cmd_ret_val=$?
    write_quote_str "${cmd_output_str}"
    return $cmd_ret_val
    }

    write_title() {
    local level="$1"
    local title="$2"
    local prefix='######'
    prefix="${prefix::$level}"
    ((__need_blank_line)) && echo
    print_tty_ctrl '01;34'
    echo "${prefix} ${title}" | replace_reset '01;34'
    print_tty_ctrl '0'
    __need_blank_line=0
    set_cur_level -1
    }

    write_order_list() {
    local str="$1"
    local index
    increase_cur_level -1
    increase_cur_level 1
    ((index = ++__list_indexes[__current_level - 1]))
    ((${#index} > 2)) && index="${index: -2}"
    index="${index}. "
    increase_cur_level -1
    write_paragraph "${str}" "${index::4}" ' ' '01;32'
    increase_cur_level 1
    }

    write_order_list_eval() {
    write_order_list "$(print_eval "$@")"
    }

    # write_list() {
    # local str="$1"
    # increase_cur_level -1
    # write_paragraph "${str}" '* ' ' ' '01;32'
    # increase_cur_level 1
    # }


    #############################
    # print tips and links
    #############################

    wiki_url="http://fcitx-im.org/wiki"

    beginner_guide_link() {
    print_link "$(_ "Beginner's Guide")" \
    "${wiki_url}$(_ /Beginner%27s_Guide)"
    }

    set_env_link() {
    local env_name="$1"
    local value="$2"
    local fmt
    fmt=$(_ 'Please set environment variable ${env_name} to "${value}" using the tool your distribution provides or add ${1} to your ${2}. See ${link}.')
    local link
    link=$(print_link \
    "$(_ 'Input Method Related Environment Variables: ')${env_name}" \
    "${wiki_url}$(_ '/Input_method_related_environment_variables')#${env_name}")
    write_error_eval "${fmt}" "$(code_inline "export ${env_name}=${value}")" \
    "$(code_inline '~/.xprofile')"
    }

    gnome_36_check_gsettings() {
    gsettings get org.gnome.settings-daemon.plugins.keyboard \
    active 2> /dev/null || return 1
    }

    gnome_36_link() {
    # Do nothing if the DE is not gnome3
    maybe_gnome3 || return 1
    local link ibus_activated fmt
    link=$(print_link \
    "$(_ 'Note for GNOME Later than 3.6')" \
    "${wiki_url}$(_ '/Note_for_GNOME_Later_than_3.6')")

    # Check if the gsettings key exists
    if ibus_activated=$(gnome_36_check_gsettings); then
    [[ $ibus_activated = 'false' ]] && return 1
    g36_disable_ibus=$(code_inline 'gsettings set org.gnome.settings-daemon.plugins.keyboard active false')
    fmt=$(_ 'If you are using ${1}, you may want to uninstall ${2}, remove ${3} or use the command ${g36_disable_ibus} to disable IBus integration in order to use any input method other than ${2}. See ${link} for more detail.')
    else
    fmt=$(_ 'If you are using ${1}, you may want to uninstall ${2} or remove ${3} in order to use any input method other than ${2}. See ${link} for more detail as well as alternative solutions.')
    fi
    write_error_eval "${fmt}" "$(code_inline 'gnome>=3.6')" \
    "$(code_inline 'ibus')" "$(code_inline 'ibus-daemon')"
    }

    no_xim_link() {
    local fmt
    fmt=$(_ 'To see some application specific problems you may have when using xim, check ${link1}. For other more general problems of using XIM including application freezing, see ${link2}.')
    local link1
    link1=$(print_link \
    "$(_ 'Hall of Shame for Linux IME Support')" \
    "${wiki_url}$(_ '/Hall_of_Shame_for_Linux_IME_Support')")
    local link2
    link2=$(print_link \
    "$(_ 'here')" \
    "${wiki_url}$(_ '/XIM')")
    write_error_eval "${fmt}"
    }


    #############################
    # system info
    #############################

    ldpaths=()
    init_ld_paths() {
    local IFS=$'\n'
    unique_file_array ldpath ldpaths $(ldconfig -p 2> /dev/null | grep '=>' | \
    sed -e 's:.* => \(.*\)/[^/]*$:\1:g' | sort -u) \
    {/usr,,/usr/local}/lib*
    }
    init_ld_paths

    fcitx_lib_path=()
    init_fcitx_lib_path() {
    local path
    local __fcitx_lib_path
    for path in "$(fcitx4-config "--libdir" 2> /dev/null)" "${ldpaths[@]}"; do
    [ ! -z "${path}" ] && [ -d "${path}/fcitx5/" ] && {
    __fcitx_lib_path=("${__fcitx_lib_path[@]}" "${path}/fcitx5/")
    }
    done 2> /dev/null
    unique_file_array _fcitx_lib_path fcitx_lib_path "${__fcitx_lib_path[@]}"
    }
    init_fcitx_lib_path

    find_fcitx_lib() {
    find_file "$1" -H "${fcitx_lib_path[@]}" -name "$2"
    }

    check_system() {
    write_title 1 "$(_ 'System Info:')"
    write_order_list "$(code_inline 'uname -a'):"
    if type uname &> /dev/null; then
    write_quote_cmd uname -a
    else
    write_error "$(print_not_found 'uname')"
    fi
    if type lsb_release &> /dev/null; then
    write_order_list "$(code_inline 'lsb_release -a'):"
    write_quote_cmd lsb_release -a
    write_order_list "$(code_inline 'lsb_release -d'):"
    write_quote_cmd lsb_release -d
    else
    write_order_list "$(code_inline lsb_release):"
    write_paragraph "$(print_not_found 'lsb_release')"
    fi
    write_order_list "$(code_inline /etc/lsb-release):"
    if [ -f /etc/lsb-release ]; then
    write_quote_cmd cat /etc/lsb-release
    else
    write_paragraph "$(print_not_found '/etc/lsb-release')"
    fi
    write_order_list "$(code_inline /etc/os-release):"
    if [ -f /etc/os-release ]; then
    write_quote_cmd cat /etc/os-release
    else
    write_paragraph "$(print_not_found '/etc/os-release')"
    fi
    write_order_list "$(_ 'Desktop Environment:')"
    # TODO check unity
    if [[ -z $DE ]] || [[ $DE = generic ]]; then
    write_eval "$(_ 'Cannot determine desktop environment.')"
    else
    write_eval "$(_ 'Desktop environment is ${1}.')" \
    "$(code_inline "${DE}")"
    fi
    write_order_list "$(_ 'Bash Version:')"
    write_quote_str "BASH_VERSION='${BASH_VERSION}'"
    }

    check_env() {
    write_title 1 "$(_ 'Environment:')"
    write_order_list "DISPLAY:"
    write_quote_str "DISPLAY='${DISPLAY}'"
    write_order_list "$(_ 'Keyboard Layout:')"
    increase_cur_level 1
    write_order_list "$(code_inline setxkbmap):"
    if type setxkbmap &> /dev/null; then
    write_quote_cmd setxkbmap -print
    else
    write_paragraph "$(print_not_found 'setxkbmap')"
    fi
    write_order_list "$(code_inline xprop):"
    if type xprop &> /dev/null; then
    write_quote_cmd xprop -root _XKB_RULES_NAMES
    else
    write_paragraph "$(print_not_found 'xprop')"
    fi
    increase_cur_level -1
    write_order_list "$(_ 'Locale:')"
    if type locale &> /dev/null; then
    increase_cur_level 1
    write_order_list "$(_ 'All locale:')"
    write_quote_str "$(locale -a 2> /dev/null)"
    write_order_list "$(_ 'Current locale:')"
    write_quote_str "$(locale 2> /dev/null)"
    locale_error="$(locale 2>&1 > /dev/null)"
    if [[ -n $locale_error ]]; then
    write_error_eval "$(_ 'Error occurs when running ${1}. Please check your locale settings.')" \
    "$(code_inline "locale")"
    write_quote_str "${locale_error}"
    fi
    increase_cur_level -1
    else
    write_paragraph "$(print_not_found 'locale')"
    fi
    write_order_list "$(_ 'Directories:')"
    increase_cur_level 1
    write_order_list "$(_ 'Home:')"
    write_quote_str ~
    write_order_list "$(code_inline '${XDG_CONFIG_HOME}'):"
    if [[ -z ${XDG_CONFIG_HOME} ]]; then
    write_eval "$(_ 'Environment variable ${1} is not set.')" \
    "$(code_inline 'XDG_CONFIG_HOME')"
    else
    write_eval \
    "$(_ 'Environment variable ${1} is set to ${2}.')" \
    "$(code_inline 'XDG_CONFIG_HOME')" \
    "$(code_inline "${XDG_CONFIG_HOME}")"
    fi
    write_eval "$(_ 'Current value of ${1} is ${2} (${3}).')" \
    "$(code_inline 'XDG_CONFIG_HOME')" \
    "$(code_inline "${xdg_conf_pretty_name}")" \
    "$(code_inline "${_xdg_conf_home}")"
    write_order_list "$(_ "Fcitx Settings Directory:")"
    write_eval \
    "$(_ 'Current fcitx settings directory is ${1} (${2}).')" \
    "$(code_inline "${fx_conf_pretty_name}")" \
    "$(code_inline "${fx_conf_home}")"
    increase_cur_level -1

    write_order_list "$(_ 'Current user:')"
    write_eval "$(_ 'The script is run as ${1} (${2}).')" \
    "${cur_user}" "${cur_uid}"
    if check_is_root; then
    increase_cur_level 1
    local has_sudo_var=0
    write_order_list_eval "$(_ '${1} Environment Variables:')" \
    "$(code_inline 'sudo')"
    check_sudo_env() {
    local env_name=${1}
    if [[ -n ${!env_name} ]]; then
    has_sudo_var=1
    write_eval "$(_ '${1} is set to ${2}.')" \
    "${env_name}" "${!env_name}"
    else
    write_eval "$(_ '${1} is not set.')" "${env_name}"
    fi
    }
    check_sudo_env SUDO_COMMAND
    check_sudo_env SUDO_USER
    check_sudo_env SUDO_UID
    check_sudo_env SUDO_GID
    write_order_list "$(_ 'Running as root:')"
    if ((has_sudo_var)); then
    write_error_eval \
    "$(_ 'You are probably using ${1} to run this script. This means the result of this script may not be accurate. See ${2} for more information.')" \
    "$(code_inline 'sudo')" \
    "$(print_google_link "$(_ "sudo environment variables")")"
    else
    write_error_eval \
    "$(_ 'You are probably logging in as ${1} or using ${2} to run this script. This either means you have security problems or the result of this script may not be accurate. See ${3} or ${4} for more information.')" \
    "$(code_inline 'root')" "$(code_inline 'sudo')" \
    "$(print_google_link "$(_ "Why is it bad to run as root")")" \
    "$(print_google_link "$(_ "sudo environment variables")")"
    fi
    increase_cur_level -1
    fi
    }

    check_fcitx() {
    local IFS=$'\n'
    write_title 1 "$(_ 'Fcitx State:')"
    write_order_list "$(_ 'executable:')"
    if ! fcitx_exe="$(which fcitx5 2> /dev/null)"; then
    write_error "$(_ 'Cannot find fcitx5 executable!')"
    __need_blank_line=0
    write_error_eval "$(_ 'Please check ${1} for how to install fcitx.')" \
    "$(beginner_guide_link)"
    exit 1
    else
    write_eval "$(_ 'Found fcitx5 at ${1}.')" "$(code_inline "${fcitx_exe}")"
    fi
    write_order_list "$(_ 'version:')"
    version=$(fcitx5 -v 2> /dev/null | \
    sed -e 's/.*fcitx version: \([0-9.]*\).*/\1/g')
    write_eval "$(_ 'Fcitx version: ${1}')" "$(code_inline "${version}")"
    write_order_list "$(_ 'process:')"
    psoutput=$(ps -Ao pid,comm)
    process=()
    while read line; do
    if [[ $line =~ ^([0-9]*)\ .*fcitx.* ]]; then
    [ "${BASH_REMATCH[1]}" = "$$" ] && continue
    array_push process "${line}"
    fi
    done <<< "${psoutput}"
    if ! ((${#process[@]})); then
    write_error "$(_ 'Fcitx is not running.')"
    __need_blank_line=0
    write_error_eval "$(_ 'Please check the Configure link of your distribution in ${1} for how to setup fcitx autostart.')" "$(beginner_guide_link)"
    return 1
    fi
    local pcount="${#process[@]}"
    if ((pcount > 1)); then
    write_eval "$(_ 'Found ${1} fcitx processes:')" "${#process[@]}"
    else
    write_eval "$(_ 'Found ${1} fcitx process:')" "${#process[@]}"
    fi
    write_quote_cmd print_array "${process[@]}"
    write_order_list "$(code_inline 'fcitx5-remote'):"
    if type fcitx5-remote &> /dev/null; then
    if ! fcitx5-remote &> /dev/null; then
    write_error "$(_ 'Cannot connect to fcitx correctly.')"
    else
    write_eval "$(_ '${1} works properly.')" \
    "$(code_inline 'fcitx5-remote')"
    fi
    else
    write_error "$(print_not_found "fcitx5-remote")"
    fi
    write_order_list "$(_ "DBus interface:")"
    if [[ -n ${dbus_exe} ]]; then
    write_eval "$(_ 'Using ${1} to check dbus.')" \
    "$(code_inline "${dbus_exe}")"
    owner_name=$(dbus_get_name_owner org.fcitx.Fcitx5)
    owner_pid=$(dbus_get_pid org.fcitx.Fcitx5)
    if [[ -n ${owner_name} ]]; then
    write_eval "$(_ 'Owner of DBus name ${1} is ${2}.')" \
    "$(code_inline 'org.fcitx.Fcitx5')" \
    "$(code_inline "${owner_name}")"
    else
    write_error_eval "$(_ 'Cannot find DBus name ${1} owner.')" \
    "$(code_inline 'org.fcitx.Fcitx5')"
    fi
    if [[ -n ${owner_pid} ]]; then
    write_eval "$(_ 'PID of DBus name ${1} owner is ${2}.')" \
    "$(code_inline 'org.fcitx.Fcitx5')" \
    "$(code_inline "${owner_pid}")"
    else
    write_error_eval "$(_ 'Cannot find pid of DBus name ${1} owner.')" \
    "$(code_inline 'org.fcitx.Fcitx5')"
    fi
    else
    write_error "$(_ "Unable to find a program to check dbus.")"
    fi
    }

    _find_config_gtk() {
    [ -n "${_config_tool_gtk_exe}" ] && {
    echo "${_config_tool_gtk_exe}"
    return 0
    }
    local config_gtk
    config_gtk="$(which "fcitx-config-gtk" 2> /dev/null)" || return 1
    echo "${config_gtk}"
    _config_tool_gtk_exe="${config_gtk}"
    }

    _check_config_gtk_version() {
    local version=$1
    local config_gtk
    [ -z "${_config_tool_gtk_version}" ] && {
    config_gtk="$(_find_config_gtk)" || return 1
    ld_info="$(ldd "$config_gtk" 2> /dev/null)" ||
    ld_info="$(objdump -p "$config_gtk" 2> /dev/null)" || return 1
    if [[ $ld_info =~ libgtk[-._a-zA-Z0-9]*3[-._a-zA-Z0-9]*\.so ]]; then
    _config_tool_gtk_version=3
    elif [[ $ld_info =~ libgtk[-._a-zA-Z0-9]*2[-._a-zA-Z0-9]*\.so ]]; then
    _config_tool_gtk_version=2
    else
    return 1
    fi
    }
    [ "${_config_tool_gtk_version}" = "$version" ]
    }

    _check_config_gtk() {
    local version=$1
    local config_gtk config_gtk_name
    write_order_list_eval "$(_ 'Config GUI for gtk${1}:')" "${version}"
    if ! config_gtk="$(which "fcitx-config-gtk${version}" 2> /dev/null)"; then
    if ! _check_config_gtk_version "${version}"; then
    write_error_eval \
    "$(_ 'Config GUI for gtk${1} not found.')" "${version}"
    return 1
    else
    config_gtk=$(_find_config_gtk)
    config_gtk_name="fcitx-config-gtk"
    fi
    else
    config_gtk_name="fcitx-config-gtk${version}"
    fi
    write_eval "$(_ 'Found ${1} at ${2}.')" \
    "$(code_inline "${config_gtk_name}")" \
    "$(code_inline "${config_gtk}")"
    }

    _check_config_kcm() {
    local kcm_shell config_kcm
    write_order_list "$(_ 'Config GUI for kde:')"
    if ! kcm_shell="$(which "kcmshell5" 2> /dev/null)"; then
    write_error "$(print_not_found 'kcmshell4')"
    return 1
    fi
    config_kcm="$(kcmshell5 --list 2> /dev/null | grep -i fcitx)" && {
    write_paragraph "$(_ 'Found fcitx kcm module.')"
    write_quote_str "${config_kcm}"
    return 0
    }
    return 1
    }

    check_config_ui() {
    local IFS=$'\n'
    write_title 1 "$(_ 'Fcitx Configure UI:')"
    write_order_list "$(_ 'Config Tool Wrapper:')"
    if ! fcitx_configtool="$(which fcitx5-configtool 2> /dev/null)"; then
    write_error "$(_ 'Cannot find fcitx5-configtool executable!')"
    else
    write_eval "$(_ 'Found fcitx5-configtool at ${1}.')" \
    "$(code_inline "${fcitx_configtool}")"
    fi
    local config_backend_found=0
    _check_config_gtk 2 && config_backend_found=1
    _check_config_gtk 3 && config_backend_found=1
    _check_config_kcm && config_backend_found=1
    if ((!config_backend_found)) && [[ -n "$DISPLAY$WAYLAND_DISPLAY" ]]; then
    write_error_eval "$(_ 'Cannot find a GUI config tool, please install one of ${1}, ${2}, or ${3}.')" \
    "$(code_inline kcm-fcitx)" "$(code_inline fcitx-config-gtk2)" \
    "$(code_inline fcitx-config-gtk3)"
    fi
    }


    #############################
    # front end
    #############################

    _env_correct() {
    write_eval \
    "$(_ 'Environment variable ${1} is set to "${2}" correctly.')" \
    "$1" "$2"
    }

    _env_incorrect() {
    write_error_eval \
    "$(_ 'Environment variable ${1} is "${2}" instead of "${3}". Please check if you have exported it incorrectly in any of your init files.')" \
    "$1" "$3" "$2"
    }

    check_xim() {
    write_title 2 "Xim:"
    xim_name=fcitx
    write_order_list "$(code_inline '${XMODIFIERS}'):"
    if [ -z "${XMODIFIERS}" ]; then
    write_error_eval "$(_ 'XMODIFIERS is not set')"
    set_env_link XMODIFIERS '@im=fcitx'
    __need_blank_line=0
    elif [ "${XMODIFIERS}" = '@im=fcitx' ]; then
    _env_correct 'XMODIFIERS' '@im=fcitx'
    __need_blank_line=0
    else
    _env_incorrect 'XMODIFIERS' '@im=fcitx' "${XMODIFIERS}"
    set_env_link XMODIFIERS '@im=fcitx'
    if [[ ${XMODIFIERS} =~ @im=([-_0-9a-zA-Z]+) ]]; then
    xim_name="${BASH_REMATCH[1]}"
    else
    __need_blank_line=0
    write_error_eval "$(_ 'Cannot interpret XMODIFIERS: ${1}.')" \
    "${XMODIFIERS}"
    fi
    if [[ ${xim_name} = ibus ]]; then
    __need_blank_line=0
    gnome_36_link || __need_blank_line=1
    fi
    fi
    write_eval "$(_ 'Xim Server Name from Environment variable is ${1}.')" \
    "${xim_name}"
    write_order_list "$(_ 'XIM_SERVERS on root window:')"
    local atom_name=XIM_SERVERS
    if ! type xprop &> /dev/null; then
    write_error "$(print_not_found 'xprop')"
    else
    xprop=$(xprop -root -notype -f "${atom_name}" \
    '32a' ' $0\n' "${atom_name}" 2> /dev/null)
    if [[ ${xprop} =~ ^${atom_name}\ @server=(.*)$ ]]; then
    xim_server_name="${BASH_REMATCH[1]}"
    if [ "${xim_server_name}" = "${xim_name}" ]; then
    write_paragraph "$(_ 'Xim server name is the same with that set in the environment variable.')"
    else
    write_error_eval "$(_ 'Xim server name: "${1}" is different from that set in the environment variable: "${2}".')" \
    "${xim_server_name}" "${xim_name}"
    fi
    else
    write_error "$(_ 'Cannot find xim_server on root window.')"
    fi
    fi
    local _LC_CTYPE=$(get_locale CTYPE)
    if type emacs &> /dev/null &&
    ! str_match_regex '^(zh|ja|ko)([._].*|)$' "${_LC_CTYPE}"; then
    write_order_list "$(_ 'XIM for Emacs:')"
    write_error_eval \
    "$(_ 'Your LC_CTYPE is set to ${1} instead of one of zh, ja, ko. You may not be able to use input method in emacs because of an really old emacs bug that upstream refuse to fix for years.')" "${_LC_CTYPE}"
    fi
    if ! str_match_regex '.[Uu][Tt][Ff]-?8$' "${_LC_CTYPE}"; then
    write_order_list "$(_ 'XIM encoding:')"
    write_error_eval \
    "$(_ 'Your LC_CTYPE is set to ${1} whose encoding is not UTF-8. You may have trouble committing strings using XIM.')" "${_LC_CTYPE}"
    fi
    }

    _check_toolkit_env() {
    local name="$1"
    local env_names=("${@:2}")
    local env_name
    write_order_list "${name} - $(code_inline '${'"${env_names[0]}"'}'):"
    for env_name in "${env_names[@]}"; do
    [ -z "${!env_name}" ] || break
    done
    if [ -z "${!env_name}" ]; then
    set_env_link "${env_name}" 'fcitx'
    elif [ "${!env_name}" = 'fcitx' ]; then
    _env_correct "${env_name}" 'fcitx'
    else
    _env_incorrect "${env_name}" 'fcitx' "${!env_name}"
    __need_blank_line=0
    if [ "${!env_name}" = 'xim' ]; then
    write_error_eval "$(_ 'You are using xim in ${1} programs.')" \
    "${name}"
    no_xim_link
    else
    write_error_eval \
    "$(_ 'You may have trouble using fcitx in ${1} programs.')" \
    "${name}"
    if [ "${!env_name}" = "ibus" ] && [ "${name}" = 'qt' ]; then
    __need_blank_line=0
    gnome_36_link || __need_blank_line=1
    fi
    fi
    set_env_link "${env_name}" 'fcitx'
    fi
    }

    find_qt_modules() {
    local qt_dirs _qt_modules
    find_file qt_dirs -H "${ldpaths[@]}" -type d -name '*qt*'
    find_file _qt_modules -H "${qt_dirs[@]}" -type f -iname '*fcitx*.so'
    qt_modules=()
    unique_file_array qt_modules qt_modules "${_qt_modules[@]}"
    }

    check_qt() {
    write_title 2 "Qt:"
    _check_toolkit_env qt4 QT4_IM_MODULE QT_IM_MODULE
    _check_toolkit_env qt5 QT_IM_MODULE
    find_qt_modules
    qt4_module_found=''
    qt5_module_found=''
    write_order_list "$(_ 'Qt IM module files:')"
    echo
    for file in "${qt_modules[@]}"; do
    basename=$(basename "${file}")
    __need_blank_line=0
    if [[ ${basename} =~ im-fcitx ]] &&
    [[ ${file} =~ plugins/inputmethods ]]; then
    write_eval "$(_ 'Found fcitx im module for ${2}: ${1}.')" \
    "$(code_inline "${file}")" Qt4
    qt4_module_found=1
    elif [[ ${basename} =~ fcitx5platforminputcontextplugin ]] &&
    [[ ${file} =~ plugins/platforminputcontexts ]]; then
    write_eval "$(_ 'Found fcitx5 im module for ${2}: ${1}.')" \
    "$(code_inline "${file}")" Qt5
    qt5_module_found=1
    elif [[ ${file} =~ /fcitx5/qt/ ]]; then
    write_eval "$(_ 'Found fcitx5 qt module: ${1}.')" \
    "$(code_inline "${file}")"
    else
    write_eval "$(_ 'Found unknown fcitx5 qt module: ${1}.')" \
    "$(code_inline "${file}")"
    fi
    done
    if [ -z "${qt4_module_found}" ]; then
    __need_blank_line=0
    write_error_eval \
    "$(_ 'Cannot find fcitx input method module for ${1}.')" Qt4
    fi
    if [ -z "${qt5_module_found}" ]; then
    __need_blank_line=0
    write_error_eval \
    "$(_ 'Cannot find fcitx input method module for ${1}.')" Qt5
    fi
    }

    init_gtk_dirs() {
    local gtk_dirs_name="__gtk${version}_dirs"
    eval '((${#'"${gtk_dirs_name}"'[@]}))' || {
    find_file "${gtk_dirs_name}" -H "${ldpaths[@]}" -type d \
    '(' -name "gtk-${version}*" -o -name 'gtk' ')'
    }
    eval 'gtk_dirs=("${'"${gtk_dirs_name}"'[@]}")'
    }

    find_gtk_query_immodules() {
    local version="$1"
    init_gtk_dirs "${version}"
    local IFS=$'\n'
    local query_im_lib
    find_file query_im_lib -H "${gtk_dirs[@]}" -type f \
    -name "gtk-query-immodules-${version}*"
    gtk_query_immodules=()
    unique_file_array "gtk_query_immodules_${version}" gtk_query_immodules \
    $(find_in_path "gtk-query-immodules-${version}*") \
    "${query_im_lib[@]}"
    }

    reg_gtk_query_output() {
    local version="$1"
    while read line; do
    regex='"(/[^"]*\.so)"'
    [[ $line =~ $regex ]] || continue
    file=${BASH_REMATCH[1]}
    add_and_check_file "__gtk_immodule_files_${version}" "${file}" && {
    array_push "gtk_immodule_files_${version}" "${file}"
    }
    done <<< "$2"
    }

    check_gtk_immodule_file() {
    local version=$1
    local gtk_immodule_files
    local all_exists=1
    write_order_list "gtk ${version}:"
    eval 'gtk_immodule_files=("${gtk_immodule_files_'"${version}"'[@]}")'
    for file in "${gtk_immodule_files[@]}"; do
    [[ -f "${file}" ]] || {
    all_exists=0
    write_error_eval \
    "$(_ 'Gtk ${1} immodule file ${2} does not exist.')" \
    "${version}" \
    "${file}"
    }
    done
    ((all_exists)) && \
    write_eval "$(_ 'All found Gtk ${1} immodule files exist.')" \
    "${version}"
    }

    check_gtk_query_immodule() {
    local version="$1"
    local IFS=$'\n'
    find_gtk_query_immodules "${version}"
    local module_found=0
    local query_found=0
    write_order_list "gtk ${version}:"

    for query_immodule in "${gtk_query_immodules[@]}"; do
    query_output=$("${query_immodule}")
    real_version=''
    version_line=''
    while read line; do
    regex='[Cc]reated.*gtk-query-immodules.*gtk\+-*([0-9][^ ]+)$'
    [[ $line =~ $regex ]] && {
    real_version="${BASH_REMATCH[1]}"
    version_line="${line}"
    break
    }
    done <<< "${query_output}"
    if [[ -n $version_line ]]; then
    regex="^${version}\."
    if [[ $real_version =~ $regex ]]; then
    query_found=1
    write_command=write_eval
    else
    write_command=write_error_eval
    fi
    "$write_command" \
    "$(_ 'Found ${3} for gtk ${1} at ${2}.')" \
    "$(code_inline "${real_version}")" \
    "$(code_inline "${query_immodule}")" \
    "$(code_inline gtk-query-immodules)"
    __need_blank_line=0
    write_eval "$(_ 'Version Line:')"
    write_quote_str "${version_line}"
    else
    write_eval "$(_ 'Found ${2} for unknown gtk version at ${1}.')" \
    "$(code_inline "${query_immodule}")" \
    "$(code_inline gtk-query-immodules)"
    real_version=${version}
    fi
    if fcitx_gtk=$(grep fcitx <<< "${query_output}"); then
    module_found=1
    __need_blank_line=0
    write_eval "$(_ 'Found fcitx im modules for gtk ${1}.')" \
    "$(code_inline ${real_version})"
    write_quote_str "${fcitx_gtk}"
    reg_gtk_query_output "${version}" "${fcitx_gtk}"
    else
    write_error_eval \
    "$(_ 'Failed to find fcitx in the output of ${1}')" \
    "$(code_inline "${query_immodule}")"
    fi
    done
    ((query_found)) || {
    write_error_eval \
    "$(_ 'Cannot find ${2} for gtk ${1}')" \
    "${version}" \
    "$(code_inline gtk-query-immodules)"
    }
    ((module_found)) || {
    write_error_eval \
    "$(_ 'Cannot find fcitx im module for gtk ${1}.')" \
    "${version}"
    }
    }

    find_gtk_immodules_cache() {
    local version="$1"
    init_gtk_dirs "${version}"
    local IFS=$'\n'
    local __gtk_immodule_cache
    find_file __gtk_immodule_cache -H \
    "${gtk_dirs[@]}" /etc/gtk-${version}* -type f \
    '(' -name '*gtk.immodules*' -o -name '*immodules.cache*' ')'
    unique_file_array "gtk_immodules_cache_${version}" "$2" \
    "${__gtk_immodule_cache[@]}"
    }

    check_gtk_immodule_cache() {
    local version="$1"
    local IFS=$'\n'
    local cache_found=0
    local module_found=0
    local version_correct=0
    write_order_list "gtk ${version}:"
    local gtk_immodules_cache
    find_gtk_immodules_cache "${version}" gtk_immodules_cache

    for cache in "${gtk_immodules_cache[@]}"; do
    cache_content=$(cat "${cache}")
    real_version=''
    version_line=''
    version_correct=0
    while read line; do
    regex='[Cc]reated.*gtk-query-immodules.*gtk\+-*([0-9][^ ]+)$'
    [[ $line =~ $regex ]] && {
    real_version="${BASH_REMATCH[1]}"
    version_line="${line}"
    break
    }
    done <<< "${cache_content}"
    if [[ -n $version_line ]]; then
    regex="^${version}\."
    if [[ $real_version =~ $regex ]]; then
    cache_found=1
    version_correct=1
    write_command=write_eval
    else
    write_command=write_error_eval
    fi
    "$write_command" \
    "$(_ 'Found immodules cache for gtk ${1} at ${2}.')" \
    "$(code_inline ${real_version})" \
    "$(code_inline "${cache}")"
    __need_blank_line=0
    write_eval "$(_ 'Version Line:')"
    write_quote_str "${version_line}"
    else
    write_eval \
    "$(_ 'Found immodule cache for unknown gtk version at ${1}.')" \
    "$(code_inline "${cache}")"
    real_version=${version}
    fi
    if fcitx_gtk=$(grep fcitx <<< "${cache_content}"); then
    ((version_correct)) && module_found=1
    __need_blank_line=0
    write_eval "$(_ 'Found fcitx im modules for gtk ${1}.')" \
    "$(code_inline ${real_version})"
    write_quote_str "${fcitx_gtk}"
    reg_gtk_query_output "${version}" "${fcitx_gtk}"
    else
    write_error_eval \
    "$(_ 'Failed to find fcitx in immodule cache at ${1}')" \
    "$(code_inline "${cache}")"
    fi
    done
    ((cache_found)) || {
    write_error_eval \
    "$(_ 'Cannot find immodules cache for gtk ${1}')" \
    "${version}"
    }
    ((module_found)) || {
    write_error_eval \
    "$(_ 'Cannot find fcitx im module for gtk ${1} in cache.')" \
    "${version}"
    }
    }

    check_gtk() {
    write_title 2 "Gtk:"
    _check_toolkit_env gtk GTK_IM_MODULE
    write_order_list "$(code_inline gtk-query-immodules):"
    increase_cur_level 1
    check_gtk_query_immodule 2
    check_gtk_query_immodule 3
    increase_cur_level -1
    write_order_list "$(_ 'Gtk IM module cache:')"
    increase_cur_level 1
    check_gtk_immodule_cache 2
    check_gtk_immodule_cache 3
    increase_cur_level -1
    write_order_list "$(_ 'Gtk IM module files:')"
    increase_cur_level 1
    check_gtk_immodule_file 2
    check_gtk_immodule_file 3
    increase_cur_level -1
    }


    #############################
    # fcitx modules
    #############################

    check_modules() {
    local addon_conf_dir
    write_title 2 "$(_ 'Fcitx Addons:')"
    write_order_list "$(_ 'Addon Config Dir:')"
    addon_conf_dir="$(get_config_dir addonconfigdir addon)" || {
    write_error "$(_ 'Cannot find fcitx addon config directory.')"
    return
    }
    local enabled_addon=()
    local disabled_addon=()
    local enabled_ui=()
    local addon_file
    local name
    local enable
    declare -A addon_file
    write_eval "$(_ 'Found fcitx addon config directory: ${1}.')" \
    "$(code_inline "${addon_conf_dir}")"
    write_order_list "$(_ 'Addon List:')"
    for file in "${addon_conf_dir}"/*.conf; do
    if ! name=$(get_from_config_file "${file}" Name); then
    write_error_eval \
    "$(_ 'Invalid addon config file ${1}.')" \
    "$(code_inline "${file}")"
    continue
    fi
    enable=$(get_from_config_file "${file}" Enabled)
    if [[ -f ${fx_conf_home}/addon/${name}.conf ]]; then
    _enable=$(get_from_config_file \
    ${fx_conf_home}/addon/${name}.conf Enabled)
    [ -z "${_enable}" ] || enable="${_enable}"
    fi
    if [[ $(echo "${enable}" | sed -e 's/.*/\L&/g') = false ]]; then
    array_push disabled_addon "${name}"
    else
    array_push enabled_addon "${name}"
    if [[ $(get_from_config_file "${file}" Category) = UI ]]; then
    array_push enabled_ui "${name}"
    fi
    fi
    type=$(get_from_config_file "${file}" Type)
    if [[ -z $type ]] || [[ $type = SharedLibrary ]]; then
    addon_file["${name}"]="$(get_from_config_file "${file}" Library)"
    fi
    done
    increase_cur_level 1
    write_order_list_eval "$(_ 'Found ${1} enabled addons:')" \
    "${#enabled_addon[@]}"
    [ "${#enabled_addon[@]}" = 0 ] || {
    write_quote_cmd print_array "${enabled_addon[@]}"
    }
    write_order_list_eval "$(_ 'Found ${1} disabled addons:')" \
    "${#disabled_addon[@]}"
    [ "${#disabled_addon[@]}" = 0 ] || {
    write_quote_cmd print_array "${disabled_addon[@]}"
    }
    increase_cur_level -1
    write_order_list_eval "$(_ 'Addon Libraries:')"
    local all_module_ok=1
    local module_files
    local _module_file
    for addon_name in "${!addon_file[@]}"; do
    find_fcitx_lib module_files "${addon_file[${addon_name}]}.so"
    if [[ ${#module_files[@]} = 0 ]]; then
    write_error_eval \
    "$(_ 'Cannot find file ${1} of addon ${2}.')" \
    "$(code_inline "${addon_file[${addon_name}]}")" \
    "$(code_inline ${addon_name})"
    all_module_ok=0
    continue
    fi
    for _module_file in "${module_files[@]}"; do
    not_found="$(run_ldd "${_module_file}")"
    [[ -z ${not_found} ]] && continue
    write_error_eval \
    "$(_ 'Cannot find following required libraries for ${1} of addon ${2}.')" \
    "$(code_inline "${addon_file[${addon_name}]}")" \
    "$(code_inline ${addon_name})"
    write_quote_str "${not_found}"
    all_module_ok=0
    done
    done
    ((all_module_ok)) && \
    write_eval "$(_ 'All libraries for all addons are found.')"
    write_order_list_eval "$(_ 'User Interface:')"
    if ! ((${#enabled_ui[@]})); then
    write_error_eval "$(_ 'Cannot find enabled fcitx user interface!')"
    else
    write_eval "$(_ 'Found ${1} enabled user interface addons:')" \
    "${#enabled_ui[@]}"
    write_quote_cmd print_array "${enabled_ui[@]}"
    has_non_kimpanel=0
    has_kimpanel_dbus=0
    for ui in "${enabled_ui[@]}"; do
    if [[ $ui =~ kimpanel ]]; then
    pid=$(dbus_get_pid org.kde.impanel) || continue
    has_kimpanel_dbus=1
    write_eval "$(_ 'Kimpanel process:')"
    write_quote_cmd print_process_info "${pid}"
    else
    has_non_kimpanel=1
    fi
    done
    ((has_non_kimpanel)) || ((has_kimpanel_dbus)) || \
    write_error_eval \
    "$(_ 'Cannot find kimpanel dbus interface or enabled non-kimpanel user interface.')"
    fi
    increase_cur_level -1
    }

    check_input_methods() {
    write_title 2 "$(_ 'Input Methods:')"
    local imlist=($(get_from_config_file \
    ${fx_conf_home}/profile Name)) || {
    write_error "$(_ 'Cannot read im list from fcitx profile.')"
    return 0
    }
    local enabled_im=()
    local disabled_im=()
    local im
    local name
    local enable
    for im in "${imlist[@]}"; do
    enabled_im=("${enabled_im[@]}" "${im}")
    done
    write_order_list_eval "$(_ 'Found ${1} enabled input methods:')" \
    "${#enabled_im[@]}"
    [ "${#enabled_im[@]}" = 0 ] || {
    write_quote_cmd print_array "${enabled_im[@]}"
    }
    write_order_list "$(_ 'Default input methods:')"
    case "${#enabled_im[@]}" in
    0)
    write_error "$(_ "You don't have any input methods enabled.")"
    ;;
    1)
    if [[ ${enabled_im[0]} =~ ^keyboard- ]]; then
    write_eval "$(_ 'You only have one keyboard input method enabled. You may want to add another input method to input other languages.')"
    else
    write_error "$(_ 'You only have one input method enabled, please add a keyboard input method as the first one and your main input method as the second one.')"
    fi
    ;;
    *)
    if [[ ${enabled_im[0]} =~ ^keyboard- ]]; then
    write_eval \
    "$(_ 'You have a keyboard input method "${1}" correctly added as your default input method.')" \
    "${enabled_im[0]}"
    else
    write_error_eval \
    "$(_ 'Your first (default) input method is ${1} instead of a keyboard input method. You may have trouble deactivate fcitx.')" \
    "${enabled_im[0]}"
    fi
    ;;
    esac
    }


    #############################
    # log
    #############################

    check_log() {
    write_order_list "$(code_inline 'date'):"
    if type date &> /dev/null; then
    write_quote_cmd date
    else
    write_error "$(print_not_found 'date')"
    fi
    write_order_list "$(code_inline "${fx_conf_pretty_name}/log/"):"
    [ -d ${fx_conf_home}/log/ ] || {
    write_paragraph "$(print_not_found "${fx_conf_pretty_name}/log/")"
    return
    }
    write_quote_cmd ls -AlF ${fx_conf_home}/log/
    write_order_list "$(code_inline "${fx_conf_pretty_name}/log/crash.log"):"
    if [ -f ${fx_conf_home}/log/crash.log ]; then
    write_quote_cmd cat ${fx_conf_home}/log/crash.log
    else
    write_paragraph \
    "$(print_not_found "${fx_conf_pretty_name}/log/crash.log")"
    fi
    }


    #############################
    # cmd line
    #############################

    _check_frontend=1
    _check_modules=1
    _check_log=1

    _use_color=auto

    while true; do
    (($#)) || break
    arg=$1
    shift
    case "${arg}" in
    --color=@(never|false))
    _use_color=false
    ;;
    --color=auto)
    _use_color=auto
    ;;
    --color@(|=*))
    _use_color=true
    ;;
    esac
    done

    [ -z "$1" ] || exec > "$1"


    #############################
    # init output
    #############################

    check_istty


    #############################
    # run
    #############################

    check_system
    check_env
    check_fcitx
    check_config_ui

    ((_check_frontend)) && {
    write_title 1 "$(_ 'Frontends setup:')"
    check_xim
    check_qt
    check_gtk
    }

    ((_check_modules)) && {
    write_title 1 "$(_ 'Configuration:')"
    check_modules
    check_input_methods
    }

    ((_check_log)) && {
    write_title 1 "$(_ 'Log:')"
    check_log
    }