# shellcheck shell=bash # Bash completion for terractl (terraria-server-manager) # # Installed to /share/bash-completion/completions/terractl, where the # bash-completion package loads it automatically. Also works with 'sudo terractl'. # Instances directory: default, or TERRARIA_BASE from /etc/default/terractl. # The file is parsed rather than sourced, so nothing runs in your shell. _terractl_base() { local base=/var/lib/terraria/instances value if [[ -r /etc/default/terractl ]]; then value=$(sed -n 's/^[[:space:]]*TERRARIA_BASE=//p' /etc/default/terractl | tail -n 1) value=${value%\"}; value=${value#\"}; value=${value%\'}; value=${value#\'} if [[ -n $value ]]; then base=$value; fi fi printf '%s\n' "$base" } # Instance names. Reading the directory needs root or the 'terraria' group; if # it isn't readable, fall back to the terraria@ services systemd knows about. _terractl_instances() { local base conf base=$(_terractl_base) if [[ -r $base && -x $base ]]; then while IFS= read -r conf; do conf=${conf%/instance.conf} printf '%s\n' "${conf##*/}" done < <(compgen -G "$base/*/instance.conf") elif command -v systemctl >/dev/null 2>&1; then systemctl list-units --all --plain --no-legend 'terraria@*.service' 2>/dev/null | sed -n 's/^terraria@\([^ ]*\)\.service.*/\1/p' fi } # Remotes this user added with 'terractl remote add' _terractl_remotes() { local f=${TERRACTL_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/terractl}/remotes.json [[ -r $f ]] || return 0 python3 -c 'import json, sys; print("\n".join(json.load(open(sys.argv[1]))["remotes"]))' "$f" 2>/dev/null } # Instance names on a remote. This asks its daemon, so it can take a moment. _terractl_remote_instances() { timeout 5 terractl --host "$1" info 2>/dev/null :] for copy and move _terractl_places() { if [[ $cur == -* ]]; then _terractl_reply "--with-server" elif [[ $cur == *:* ]]; then mapfile -t COMPREPLY < <(compgen -P "${cur%:*}:" -W "$(_terractl_remote_instances "${cur%:*}")" -- "${cur##*:}") else mapfile -t COMPREPLY < <(compgen -W "$(_terractl_instances) $(_terractl_remotes | sed 's/$/:/')" -- "$cur") # After ":" comes an instance name, not a space if [[ ${#COMPREPLY[@]} -eq 1 && ${COMPREPLY[0]} == *: ]]; then compopt -o nospace 2>/dev/null; fi fi # Readline sees only the part after the last ':' if declare -F __ltrim_colon_completions >/dev/null 2>&1; then __ltrim_colon_completions "$cur"; fi } # Mods installed in an instance (*.tmod files), or enabled ones (enabled.json) _terractl_mods() { local mdir f mdir="$(_terractl_base)/$1/data/Mods" if [[ $2 == enabled ]]; then [[ -r $mdir/enabled.json ]] && grep -o '"[^"]*"' "$mdir/enabled.json" | tr -d '"' else while IFS= read -r f; do f=${f##*/} printf '%s\n' "${f%.tmod}" done < <(compgen -G "$mdir/*.tmod") fi } # File names, optionally only those ending in . (folders always offered) _terractl_files() { if declare -F _filedir >/dev/null 2>&1; then _filedir "$@" else compopt -o filenames 2>/dev/null if [[ -n ${1:-} ]]; then mapfile -t COMPREPLY < <(compgen -d -- "$cur"; compgen -f -X "!*.$1" -- "$cur") else mapfile -t COMPREPLY < <(compgen -f -- "$cur") fi fi } # Set COMPREPLY to the words matching what's typed so far _terractl_reply() { mapfile -t COMPREPLY < <(compgen -W "$1" -- "$cur") } _terractl() { local cur words cword if declare -F _init_completion >/dev/null 2>&1; then # shellcheck disable=SC2034 # prev is set by _init_completion but not needed here local prev # -n : keeps ":" (copy, move) in one word _init_completion -n : || return else cur=${COMP_WORDS[COMP_CWORD]} words=("${COMP_WORDS[@]}") cword=$COMP_CWORD fi local commands="new list info start stop restart enable disable attach watch cmd mods reinstall delete rename export import" local console="save say playing kick ban motd password time dawn noon dusk midnight settle seed version maxplayers port clear help" # terractl --host ...: the same as on this host, except # that instance names come from the remote and mod names aren't offered local host="" if [[ ${words[1]:-} == --host || ${words[1]:-} == -H ]]; then if (( cword == 2 )); then _terractl_reply "$(_terractl_remotes)" return fi host=${words[2]} words=("${words[0]}" "${words[@]:3}") cword=$((cword - 2)) commands+=" --help --version" else commands+=" copy move remote daemon prepare run --host --help --version" fi if (( cword == 1 )); then _terractl_reply "$commands" return fi local command=${words[1]} case $command in list|help|-h|--help|version|-V|--version) return ;; new) # new [vanilla|tmodloader] [port]: the name is new, so nothing to offer if (( cword == 3 )); then _terractl_reply "vanilla tmodloader"; fi return ;; import) # import : the name is new, so nothing to offer if (( cword == 3 )); then _terractl_files tar.gz; fi return ;; copy|move) _terractl_places return ;; remote) if (( cword == 2 )); then _terractl_reply "add list remove" elif (( cword == 3 )) && [[ ${words[2]} == remove ]]; then _terractl_reply "$(_terractl_remotes)" fi return ;; daemon) if (( cword == 2 )); then _terractl_reply "enable disable status token revoke" fi return ;; esac if (( cword == 2 )); then if [[ -n $host ]]; then _terractl_reply "$(_terractl_remote_instances "$host")" else _terractl_reply "$(_terractl_instances)" fi return fi local instance=${words[2]} case $command in cmd) if (( cword == 3 )); then _terractl_reply "$console"; fi ;; reinstall) if (( cword == 3 )); then _terractl_reply "--clean"; fi ;; enable|disable) if (( cword == 3 )); then _terractl_reply "--now"; fi ;; watch) # the number after -n is free-form if [[ $cur == -* ]]; then _terractl_reply "-n -q"; fi ;; delete) _terractl_reply "-y --yes --no-backup" ;; export) if [[ $cur == -* ]]; then _terractl_reply "--with-server"; else _terractl_files tar.gz; fi ;; mods) if (( cword == 3 )); then _terractl_reply "list add enable disable remove" return fi case ${words[3]} in add) # .tmod files and folders (e.g. the Windows export folder) _terractl_files tmod ;; enable|remove) if [[ -z $host ]]; then _terractl_reply "$(_terractl_mods "$instance" installed)"; fi ;; disable) if [[ -z $host ]]; then _terractl_reply "$(_terractl_mods "$instance" enabled)"; fi ;; esac ;; esac } complete -F _terractl terractl