mirror of
https://github.com/borgbackup/borg.git
synced 2024-12-23 08:16:54 +00:00
1644 lines
55 KiB
Text
1644 lines
55 KiB
Text
#compdef borg borgfs -P -value-,BORG_*,-default-
|
|
|
|
# Zsh completion for Borg Backup 1.2.0a9 (2020-09-27).
|
|
#
|
|
# Recommended _borg specific settings:
|
|
#
|
|
# zstyle -e ':completion:*:*:borg-*:argument-rest:*' tag-order \
|
|
# '[[ $words[CURRENT] == -* ]] && reply=( "! archives archive-files" "-" )'
|
|
# zstyle ':completion:*:*:(borg|-value-,BORG_)*' sort false
|
|
# zstyle ':completion:*:*:borg-config:argument-2:keys' list-grouped false
|
|
# zstyle ':completion:*:*:borg-*:*' gain-privileges true
|
|
# zstyle ':completion:*' fake-parameters 'BORG_REPO:scalar'
|
|
#
|
|
# Custom styles:
|
|
#
|
|
# archive-description-format
|
|
# Default: `{archive:<36} {time} [{id}]`.
|
|
# archive-sort
|
|
# In which order archive names should be listed.
|
|
# Possible values are: `inverse`, `timestamp`, `name`, `id`.
|
|
# file-description-format
|
|
# Default: `{mode} {user:6} {group:6} {size:8d} {mtime} {path}{extra}`.
|
|
# path-style-selector
|
|
# Style selector used to select a path (when Borg would use either `pp` or `pf`).
|
|
# Default: `fm`.
|
|
# repository-suffix
|
|
# This boolean style controls whether to add the `::` auto-removable suffix to a repository.
|
|
# Default: `true`.
|
|
|
|
(( $+functions[_borg_commands] )) ||
|
|
_borg_commands() {
|
|
local -a commands_=(
|
|
'benchmark:benchmark command'
|
|
'break-lock:break the repository lock'
|
|
'check:check repository consistency'
|
|
'compact:compact segment files in the repository'
|
|
'config:get, set, and delete values in a repository or cache config file'
|
|
'create:create new archive'
|
|
'debug:debugging command (not intended for normal use)'
|
|
'delete:delete an existing repository or archives'
|
|
'diff:diff contents of two archives'
|
|
'export-tar:export archive contents as a tarball'
|
|
'extract:extract archive contents'
|
|
'help:extra help'
|
|
'info:show repository or archive information'
|
|
'init:initialize an empty repository'
|
|
'key:manage repository key'
|
|
'list:list archive or repository contents'
|
|
'mount:mount archive or an entire repository as a FUSE filesystem'
|
|
'prune:prune repository archives according to specified rules'
|
|
'recreate:re-create archives'
|
|
'rename:rename an existing archive'
|
|
'serve:start in server mode'
|
|
'umount:un-mount the FUSE filesystem'
|
|
'upgrade:upgrade a repository from a previous version'
|
|
'with-lock:run a user specified command with the repository lock held'
|
|
)
|
|
_describe -t commands 'borg commands' commands_
|
|
}
|
|
|
|
(( $+functions[_borg-benchmark] )) ||
|
|
_borg-benchmark() {
|
|
local -a common_options
|
|
__borg_setup_common_options
|
|
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
':type:(crud)' \
|
|
': :_borg_repository' \
|
|
':PATH:_files'
|
|
}
|
|
|
|
(( $+functions[_borg-break-lock] )) ||
|
|
_borg-break-lock() {
|
|
local -a common_options
|
|
__borg_setup_common_options
|
|
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
':: :_borg_repository'
|
|
}
|
|
|
|
(( $+functions[_borg-check] )) ||
|
|
_borg-check() {
|
|
local -a common_options common_archive_filters_options
|
|
__borg_setup_common_options
|
|
__borg_setup_common_archive_filters_options
|
|
|
|
_arguments -s -w -S : \
|
|
'--repository-only[only perform repository checks]' \
|
|
'--archives-only[only perform archives checks]' \
|
|
'(--repository-only)--verify-data[perform cryptographic archive data integrity verification]' \
|
|
'--repair[attempt to repair any inconsistencies found]' \
|
|
'--save-space[work slower, but using less space]' \
|
|
'--max-duration=[partial repo check for max. SECONDS]: : _borg_guard_unsigned_number "SECONDS"' \
|
|
$common_archive_filters_options \
|
|
$common_options \
|
|
'::REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive'
|
|
}
|
|
|
|
(( $+functions[_borg-compact] )) ||
|
|
_borg-compact() {
|
|
local -a common_options
|
|
__borg_setup_common_options
|
|
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
'--cleanup-commits[cleanup commit-only 17-byte segment files]' \
|
|
'--threshold=[set minimum threshold for saved space in PERCENT (default: 10)]: : _borg_guard_unsigned_number "PERCENT (default\: 10)"' \
|
|
':: :_borg_repository'
|
|
}
|
|
|
|
(( $+functions[_borg-config] )) ||
|
|
_borg-config() {
|
|
local -a common_options
|
|
__borg_setup_common_options
|
|
|
|
_arguments -s -w -S : \
|
|
'(-c --cache)'{-c,--cache}'[get and set values from the repo cache]' \
|
|
'(-d --delete)'{-d,--delete}'[delete the key from the config]' \
|
|
'(-l --list)'{-l,--list}'[list the configuration of the repo]' \
|
|
$common_options \
|
|
': :_borg_repository' \
|
|
': : _borg_config $line[1]' \
|
|
'::VALUE'
|
|
}
|
|
|
|
(( $+functions[_borg-create] )) ||
|
|
_borg-create() {
|
|
local -a common_options common_create_options state line
|
|
local curcontext="$curcontext" state_descr
|
|
declare -A opt_args
|
|
local -i ret=1
|
|
__borg_setup_common_options
|
|
__borg_setup_common_create_options
|
|
|
|
local lastspec='*:PATH:_files'
|
|
(( $words[(I)--content-from-command] )) &&
|
|
lastspec='*:::PATH:->command'
|
|
|
|
_arguments -C -s -w -S : \
|
|
'*'{-e,--exclude}'=[exclude paths matching PATTERN]: : _borg_style_selector_or_archive_files -f -e "$line[1]" fm "${(@)line[2,-1]}"' \
|
|
'*--pattern=[experimental: include/exclude paths matching PATTERN]: : _borg_style_selector_or_archive_files -p -f -e "$line[1]" sh "${(@)line[2,-1]}"' \
|
|
$common_create_options \
|
|
'(-s --stats)--json[Output stats as JSON. Implies --stats.]' \
|
|
'--no-cache-sync[experimental: do not synchronize the cache. Implies not using the files cache.]' \
|
|
'--stdin-name=[use NAME in archive for stdin data (default: "stdin")]:NAME' \
|
|
'--content-from-command[interpret PATH as command and store its stdout]' \
|
|
'--exclude-nodump[exclude files flagged NODUMP]' \
|
|
'(-x --one-file-system)'{-x,--one-file-system}'[stay in the same file system]' \
|
|
'--numeric-owner[only store numeric user and group identifiers]' \
|
|
'--noatime[do not store atime into archive]' \
|
|
'--atime[do store atime into archive]' \
|
|
'--noctime[do not store ctime into archive]' \
|
|
'--nobirthtime[do not store birthtime (creation date) into archive]' \
|
|
'--nobsdflags[deprecated, use --noflags instead]' \
|
|
'--noflags[do not read and store flags (e.g. NODUMP, IMMUTABLE) into archive]' \
|
|
'--files-cache=[operate files cache in MODE. default: ctime,size,inode]:MODE:(ctime,size,inode mtime,size,inode ctime,size mtime,size rechunk,ctime rechunk,mtime disabled)' \
|
|
'--read-special[open and read block and char device files as well as FIFOs as if they were regular files]' \
|
|
$common_options \
|
|
':ARCHIVE: _borg_repository_or_archive -a -p' \
|
|
$lastspec && ret=0
|
|
|
|
case $state in
|
|
(command)
|
|
if (( CURRENT <= 1 )); then
|
|
_command_names -e && ret=0
|
|
else
|
|
_normal && ret=0
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
return ret
|
|
}
|
|
|
|
(( $+functions[_borg-debug] )) ||
|
|
_borg-debug() {
|
|
local -a state line common_options
|
|
local curcontext="$curcontext" state_descr
|
|
declare -A opt_args
|
|
local -i ret=1
|
|
__borg_setup_common_options
|
|
|
|
_arguments -s -w -C : \
|
|
$common_options \
|
|
': :->command' \
|
|
'*:: :->option-or-argument' && ret=0
|
|
|
|
case $state in
|
|
(command)
|
|
local -a debug_commands=(
|
|
'info:show system infos for debugging / bug reports'
|
|
'dump-archive-items:dump archive items (metadata)'
|
|
'dump-archive:dump decoded archive metadata'
|
|
'dump-manifest:dump decoded repository metadata'
|
|
'dump-repo-objs:dump repo objects'
|
|
'search-repo-objs:search repo objects'
|
|
'get-obj:get object from repository'
|
|
'put-obj:put object to repository'
|
|
'delete-obj:delete object from repository'
|
|
'refcount-obj:show refcount for object from repository'
|
|
'convert-profile:convert Borg profile to Python profile'
|
|
)
|
|
_describe -t commands 'command' debug_commands && ret=0
|
|
;;
|
|
(option-or-argument)
|
|
curcontext="${curcontext%:*}-$line[1]:"
|
|
|
|
case $line[1] in
|
|
(info)
|
|
_arguments -s -w -S : \
|
|
$common_options && ret=0
|
|
;;
|
|
(dump-archive-items)
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
':ARCHIVE: _borg_repository_or_archive -a' && ret=0
|
|
;;
|
|
(dump-archive)
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
':ARCHIVE: _borg_repository_or_archive -a' \
|
|
':PATH:_files' && ret=0
|
|
;;
|
|
(dump-manifest)
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
': :_borg_repository' \
|
|
':PATH:_files' && ret=0
|
|
;;
|
|
(dump-repo-objs)
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
': :_borg_repository' && ret=0
|
|
;;
|
|
(search-repo-objs)
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
': :_borg_repository' \
|
|
':WANTED (hex or string):' && ret=0
|
|
;;
|
|
(get-obj)
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
': :_borg_repository' \
|
|
':ID (hex object):' \
|
|
':PATH:_files' && ret=0
|
|
;;
|
|
(put-obj)
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
': :_borg_repository' \
|
|
'*:PATH:_files' && ret=0
|
|
;;
|
|
(delete-obj)
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
': :_borg_repository' \
|
|
'*:ID (hex object):' && ret=0
|
|
;;
|
|
(refcount-obj)
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
': :_borg_repository' \
|
|
'*:ID (hex object):' && ret=0
|
|
;;
|
|
(convert-profile)
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
':INPUT:_files' \
|
|
':OUTPUT:_files' && ret=0
|
|
;;
|
|
(*)
|
|
if ! _call_function ret _borg_debug_$line[1]; then
|
|
_default && ret=0
|
|
fi
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
|
|
return ret
|
|
}
|
|
|
|
(( $+functions[_borg-delete] )) ||
|
|
_borg-delete() {
|
|
local -a common_options common_archive_filters_options common_dry_run_stats_options
|
|
__borg_setup_common_options
|
|
__borg_setup_common_archive_filters_options
|
|
__borg_setup_common_dry_run_stats_options
|
|
|
|
_arguments -s -w -S : \
|
|
$common_dry_run_stats_options \
|
|
'--cache-only[delete only the local cache for the given repository]' \
|
|
'*--force[force deletion of corrupted archives, use "--force --force" in case "--force" does not work]' \
|
|
'--keep-security-info[keep the local security info when deleting a repository]' \
|
|
'--save-space[work slower, but using less space]' \
|
|
$common_archive_filters_options \
|
|
$common_options \
|
|
':REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' \
|
|
'*:ARCHIVE: _borg_archive "${line[1]%%::*}"'
|
|
}
|
|
|
|
(( $+functions[_borg-diff] )) ||
|
|
_borg-diff() {
|
|
local -a common_options common_exclude_options
|
|
__borg_setup_common_options
|
|
__borg_setup_common_exclude_options
|
|
|
|
_arguments -s -w -S : \
|
|
'--numeric-owner[only obey numeric user and group identifiers]' \
|
|
'--same-chunker-params[override check of chunker parameters]' \
|
|
'--sort[sort the output lines by file path]' \
|
|
$common_exclude_options \
|
|
$common_options \
|
|
':ARCHIVE1: _borg_repository_or_archive -a' \
|
|
':ARCHIVE2: _borg_archive "${line[1]%%::*}"' \
|
|
'*: : _borg_style_selector_or_archive_files -e "$line[1]" pp'
|
|
}
|
|
|
|
(( $+functions[_borg-export-tar] )) ||
|
|
_borg-export-tar() {
|
|
local -a common_options common_exclude_extract_options
|
|
__borg_setup_common_options
|
|
__borg_setup_common_exclude_extract_options
|
|
|
|
_arguments -s -w -S : \
|
|
'--tar-filter[filter program to pipe data through]: :_cmdstring' \
|
|
'--list[output verbose list of items (files, dirs, ...)]' \
|
|
$common_exclude_extract_options \
|
|
$common_options \
|
|
':ARCHIVE: _borg_repository_or_archive -a' \
|
|
':FILE:_files' \
|
|
'*: : _borg_style_selector_or_archive_files -e "$line[1]" pp'
|
|
}
|
|
|
|
(( $+functions[_borg-extract] )) ||
|
|
_borg-extract() {
|
|
local -a common_options common_exclude_extract_options
|
|
__borg_setup_common_options
|
|
__borg_setup_common_exclude_extract_options
|
|
|
|
_arguments -s -w -S : \
|
|
'--list[output verbose list of items (files, dirs, ...)]' \
|
|
'(-n --dry-run)'{-n,--dry-run}'[do not actually change any files]' \
|
|
'--numeric-owner[only obey numeric user and group identifiers]' \
|
|
'--nobsdflags[deprecated, use --noflags instead]' \
|
|
'--noflags[do not extract/set flags (e.g. NODUMP, IMMUTABLE)]' \
|
|
'--stdout[write all extracted data to stdout]' \
|
|
'--sparse[create holes in output sparse file from all-zero chunks]' \
|
|
$common_exclude_extract_options \
|
|
$common_options \
|
|
':ARCHIVE: _borg_repository_or_archive -a' \
|
|
'*: : _borg_style_selector_or_archive_files -e "$line[1]" pp'
|
|
}
|
|
|
|
(( $+functions[_borg-help] )) ||
|
|
_borg-help() {
|
|
local -a common_options
|
|
__borg_setup_common_options
|
|
|
|
_arguments -s -w -S : \
|
|
'--epilog-only' \
|
|
'--usage-only' \
|
|
$common_options \
|
|
':: : _alternative "topics:TOPIC:(patterns placeholders compression)" ": :_borg_commands"'
|
|
}
|
|
|
|
(( $+functions[_borg-info] )) ||
|
|
_borg-info() {
|
|
local -a common_options common_archive_filters_options
|
|
__borg_setup_common_options
|
|
__borg_setup_common_archive_filters_options
|
|
|
|
_arguments -s -w -S : \
|
|
'--json[format output as JSON]' \
|
|
$common_archive_filters_options \
|
|
$common_options \
|
|
'::REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive'
|
|
}
|
|
|
|
(( $+functions[_borg-init] )) ||
|
|
_borg-init() {
|
|
local -i ret=1
|
|
local -a common_options common_init_options
|
|
__borg_setup_common_options
|
|
__borg_setup_common_init_options
|
|
|
|
# special handling for the required optional argument
|
|
if (( ! ${words[(I)(-e|--encryption)(|=*)]} )); then
|
|
local desc='select encryption key mode'
|
|
local -a long=( "--encryption:$desc" ) short=( "-e:$desc" ) remove_chars=( -r '= \t\n\-' )
|
|
_describe -t required-options 'required option' long -S '=' $remove_chars -- short $remove_chars && ret=0
|
|
fi
|
|
|
|
_arguments -s -w -S : \
|
|
'(-e --encryption)'{-e,--encryption}'=[select encryption key mode (required)]:MODE:(none keyfile keyfile-blake2 repokey repokey-blake2 authenticated authenticated-blake2)' \
|
|
$common_init_options \
|
|
'--make-parent-dirs[create parent directories]' \
|
|
'::REPOSITORY:_directories' && ret=0
|
|
|
|
return ret
|
|
}
|
|
|
|
(( $+functions[_borg-key] )) ||
|
|
_borg-key() {
|
|
local -a state line common_options
|
|
local curcontext="$curcontext" state_descr
|
|
declare -A opt_args
|
|
local -i ret=1
|
|
__borg_setup_common_options
|
|
|
|
_arguments -s -w -C : \
|
|
$common_options \
|
|
': :->command' \
|
|
'*:: :->option-or-argument' && ret=0
|
|
|
|
case $state in
|
|
(command)
|
|
local -a key_commands=(
|
|
'change-passphrase:Change repository key file passphrase'
|
|
'export:Export the repository key for backup'
|
|
'import:Import the repository key from backup'
|
|
'migrate-to-repokey:Migrate passphrase -> repokey'
|
|
)
|
|
_describe -t commands 'command' key_commands && ret=0
|
|
;;
|
|
(option-or-argument)
|
|
curcontext="${curcontext%:*}-$line[1]:"
|
|
|
|
case $line[1] in
|
|
(change-passphrase)
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
': :_borg_repository' && ret=0
|
|
;;
|
|
(export)
|
|
_arguments -s -w -S : \
|
|
'--paper[create an export suitable for printing and later type-in]' \
|
|
'--qr-html[create an html file suitable for printing and later type-in or qr scan]' \
|
|
$common_options \
|
|
': :_borg_repository' \
|
|
'::PATH:_files' && ret=0
|
|
;;
|
|
(import)
|
|
_arguments -s -w -S : \
|
|
'--paper[interactively import from a backup done with --paper]' \
|
|
$common_options \
|
|
': :_borg_repository' \
|
|
'::PATH:_files' && ret=0
|
|
;;
|
|
(migrate-to-repokey)
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
':: :_borg_repository' && ret=0
|
|
;;
|
|
(*)
|
|
if ! _call_function ret _borg_key_$line[1]; then
|
|
_default && ret=0
|
|
fi
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
|
|
return ret
|
|
}
|
|
|
|
(( $+functions[_borg-list] )) ||
|
|
_borg-list() {
|
|
local -a common_options common_exclude_options common_archive_filters_options
|
|
__borg_setup_common_options
|
|
__borg_setup_common_exclude_options
|
|
__borg_setup_common_archive_filters_options
|
|
|
|
_arguments -s -w -S : \
|
|
'--consider-checkpoints[show checkpoint archives in the repository contents list (default: hidden)]' \
|
|
'--short[only print file/directory names, nothing else]' \
|
|
{--format,--list-format}'=[specify format for file listing]:FORMAT: _borg_format_keys $line[1]' \
|
|
'--json[Only valid for listing repository contents. Format output as JSON.]' \
|
|
'--json-lines[Only valid for listing archive contents. Format output as JSON Lines.]' \
|
|
$common_archive_filters_options \
|
|
$common_exclude_options \
|
|
$common_options \
|
|
':REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' \
|
|
'*: : _borg_style_selector_or_archive_files -e "$line[1]" pp'
|
|
}
|
|
|
|
(( $+functions[_borg-mount] )) ||
|
|
_borg-mount() {
|
|
local -a common_options common_exclude_extract_options common_archive_filters_options
|
|
__borg_setup_common_options
|
|
__borg_setup_common_exclude_extract_options
|
|
__borg_setup_common_archive_filters_options
|
|
|
|
_arguments -s -w -S : \
|
|
$* \
|
|
'--consider-checkpoints[show checkpoint archives in the repository contents list (default: hidden)]' \
|
|
'(-f --foreground)'{-f,--foreground}'[stay in foreground, do not daemonize]' \
|
|
'-o[mount options]: :_fuse_values "mount options"
|
|
"versions[merged, versioned view of the files in the archives]"
|
|
"allow_damaged_files[read damaged files]"
|
|
"ignore_permissions[not enforce \"default_permissions\"]"' \
|
|
$common_archive_filters_options \
|
|
$common_exclude_extract_options \
|
|
$common_options \
|
|
':REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' \
|
|
':MOUNTPOINT:_directories' \
|
|
'*: : _borg_style_selector_or_archive_files "$line[1]" pp'
|
|
}
|
|
|
|
(( $+functions[_borg-prune] )) ||
|
|
_borg-prune() {
|
|
local -a common_options common_prefix_and_glob_archives_filter_options common_dry_run_stats_options
|
|
__borg_setup_common_options
|
|
__borg_setup_common_prefix_and_glob_archives_filter_options
|
|
__borg_setup_common_dry_run_stats_options
|
|
|
|
_arguments -s -w -S : \
|
|
$common_dry_run_stats_options \
|
|
'*--force[force pruning of corrupted archives, use "--force --force" in case "--force" does not work]' \
|
|
'--list[output verbose list of archives it keeps/prunes]' \
|
|
'--keep-within[keep all archives within this time interval]: : _borg_guard_unsigned_number "INTERVAL"' \
|
|
'(--keep-last --keep-secondly)'{--keep-last,--keep-secondly}'[number of secondly archives to keep]: : _borg_guard_unsigned_number "N"' \
|
|
'--keep-minutely[number of minutely archives to keep]: : _borg_guard_unsigned_number "N"' \
|
|
'(-H --keep-hourly)'{-H,--keep-hourly}'[number of hourly archives to keep]: : _borg_guard_unsigned_number "N"' \
|
|
'(-d --keep-daily)'{-d,--keep-daily}'[number of daily archives to keep]: : _borg_guard_unsigned_number "N"' \
|
|
'(-w --keep-weekly)'{-w,--keep-weekly}'[number of weekly archives to keep]: : _borg_guard_unsigned_number "N"' \
|
|
'(-m --keep-monthly)'{-m,--keep-monthly}'[number of monthly archives to keep]: : _borg_guard_unsigned_number "N"' \
|
|
'(-y --keep-yearly)'{-y,--keep-yearly}'[number of yearly archives to keep]: : _borg_guard_unsigned_number "N"' \
|
|
'--save-space[work slower, but using less space]' \
|
|
$common_prefix_and_glob_archives_filter_options \
|
|
$common_options \
|
|
':: :_borg_repository'
|
|
}
|
|
|
|
(( $+functions[_borg-recreate] )) ||
|
|
_borg-recreate() {
|
|
local -a common_options common_create_options
|
|
__borg_setup_common_options
|
|
__borg_setup_common_create_options
|
|
|
|
local -a mods=(
|
|
'if-different:recompress if current compression is with a different compression algorithm (the level is not considered)'
|
|
'always:recompress even if current compression is with the same compression algorithm (use this to change the compression level)'
|
|
'never:do not recompress (use this option to explicitly prevent recompression)'
|
|
)
|
|
mods=( ${(q)mods//\\/\\\\} )
|
|
mods=( ${mods//:/\\:} )
|
|
|
|
_arguments -s -w -S : \
|
|
$common_create_options \
|
|
'--target=[create a new archive with the name ARCHIVE]:ARCHIVE: _borg_placeholder_or_archive "${line[1]%%\:\:*}"' \
|
|
'--recompress=[recompress data chunks according to "MODE" and "--compression"]:MODE:'"(($mods))" \
|
|
$common_options \
|
|
':REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' \
|
|
'*: : _borg_style_selector_or_archive_files -e "$line[1]" pp'
|
|
}
|
|
|
|
(( $+functions[_borg-rename] )) ||
|
|
_borg-rename() {
|
|
local -a common_options
|
|
__borg_setup_common_options
|
|
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
':ARCHIVE: _borg_repository_or_archive -a' \
|
|
':NEWNAME'
|
|
}
|
|
|
|
(( $+functions[_borg-serve] )) ||
|
|
_borg-serve() {
|
|
local -a common_options common_init_options
|
|
__borg_setup_common_options
|
|
__borg_setup_common_init_options
|
|
|
|
_arguments -s -w -S : \
|
|
$common_init_options \
|
|
'*--restrict-to-path=[restrict repository access to PATH]:PATH:_files' \
|
|
'*--restrict-to-repository=[restrict repository access]: :_borg_repository'
|
|
}
|
|
|
|
(( $+functions[_borg-umount] )) ||
|
|
_borg-umount() {
|
|
local -a common_options
|
|
__borg_setup_common_options
|
|
|
|
_arguments -s -w -S : \
|
|
$common_options \
|
|
':MOUNTPOINT:_umountable'
|
|
}
|
|
|
|
(( $+functions[_borg-upgrade] )) ||
|
|
_borg-upgrade() {
|
|
local -a common_options
|
|
__borg_setup_common_options
|
|
|
|
_arguments -s -w -S : \
|
|
'(-n --dry-run)'{-n,--dry-run}'[do not change repository]' \
|
|
'--inplace[rewrite repository in place, with no chance of going back to older versions of the repository]' \
|
|
'--force[force upgrade]' \
|
|
'--tam[enable manifest authentication (in key and cache)]' \
|
|
'--disable-tam[disable manifest authentication (in key and cache)]' \
|
|
$common_options \
|
|
':: :_borg_repository'
|
|
}
|
|
|
|
(( $+functions[_borg-with-lock] )) ||
|
|
_borg-with-lock() {
|
|
local -a state line common_options
|
|
local curcontext="$curcontext" state_descr
|
|
declare -A opt_args
|
|
local -i ret=1
|
|
__borg_setup_common_options
|
|
|
|
_arguments -s -w -C -S : \
|
|
$common_options \
|
|
'(-): :_borg_repository' \
|
|
'(-):COMMAND: _command_names -e' \
|
|
'(-)*:ARGS:->normal' && ret=0
|
|
|
|
case $state in
|
|
(normal)
|
|
shift 2 words
|
|
(( CURRENT -= 2 ))
|
|
_normal && ret=0
|
|
;;
|
|
esac
|
|
|
|
return ret
|
|
}
|
|
|
|
(( $+functions[__borg_setup_common_options] )) ||
|
|
__borg_setup_common_options() {
|
|
typeset -ga common_options=(
|
|
'(- :)'{-h,--help}'[show this help message and exit]'
|
|
'--critical[work on log level CRITICAL]'
|
|
'--error[work on log level ERROR]'
|
|
'--warning[work on log level WARNING (default)]'
|
|
'(--info -v --verbose)'{--info,-v,--verbose}'[work on log level INFO]'
|
|
'--debug[work on log level DEBUG]'
|
|
'--debug-topic=[enable TOPIC debugging (can be specified multiple times)]:TOPIC'
|
|
'(-p --progress)'{-p,--progress}'[show progress information]'
|
|
'--log-json[Output one JSON object per log line instead of formatted text.]'
|
|
'--lock-wait=[wait at most SECONDS for acquiring a repository/cache lock (default: 1)]: : _borg_guard_unsigned_number "SECONDS"'
|
|
'--bypass-lock[bypass locking mechanism]'
|
|
'(- :)--show-version[show/log the borg version]'
|
|
'--show-rc[show/log the return code (rc)]'
|
|
'--umask=[set umask to M (local only, default: 0077)]:M'
|
|
'--remote-path=[set remote path to executable (default: "borg")]: :_cmdstring'
|
|
'--remote-ratelimit=[set remote network upload rate limit in kiByte/s (default: 0=unlimited)]: : _borg_guard_unsigned_number "RATE"'
|
|
'--consider-part-files[treat part files like normal files (e.g. to list/extract them)]'
|
|
'--debug-profile=[write execution profile in Borg format into FILE]:FILE:_files'
|
|
'--rsh=[use COMMAND instead of ssh]: :_cmdstring'
|
|
)
|
|
}
|
|
|
|
(( $+functions[__borg_setup_common_exclude_options] )) ||
|
|
__borg_setup_common_exclude_options() {
|
|
typeset -ga common_exclude_options=(
|
|
'*'{-e,--exclude}'=[exclude paths matching PATTERN]: : _borg_style_selector_or_archive_files "$line[1]" fm'
|
|
'*--exclude-from=[read exclude patterns from EXCLUDEFILE, one per line]:EXCLUDEFILE:_files'
|
|
'*--pattern=[experimental: include/exclude paths matching PATTERN]: : _borg_style_selector_or_archive_files -p "$line[1]" sh'
|
|
'*--patterns-from=[experimental: read include/exclude patterns from PATTERNFILE, one per line]:PATTERNFILE:_files'
|
|
)
|
|
}
|
|
|
|
(( $+functions[__borg_setup_common_exclude_extract_options] )) ||
|
|
__borg_setup_common_exclude_extract_options() {
|
|
local -a common_exclude_options
|
|
__borg_setup_common_exclude_options
|
|
typeset -ga common_exclude_extract_options=(
|
|
$common_exclude_options
|
|
'--strip-components=[Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped.]: : _borg_guard_unsigned_number "NUMBER"'
|
|
)
|
|
}
|
|
|
|
(( $+functions[__borg_setup_common_prefix_and_glob_archives_filter_options] )) ||
|
|
__borg_setup_common_prefix_and_glob_archives_filter_options() {
|
|
typeset -ga common_prefix_and_glob_archives_filter_options=(
|
|
'(-P --prefix -a --glob-archives)'{-P,--prefix}'=[only consider archive names starting with this prefix]:PREFIX: _borg_archive -n "${line[1]%%\:\:*}"'
|
|
'(-P --prefix)*'{-a,--glob-archives}'=[only consider archive names matching the glob]:GLOB: _borg_archive -n "${line[1]%%\:\:*}"'
|
|
)
|
|
}
|
|
|
|
(( $+functions[__borg_setup_common_archive_filters_options] )) ||
|
|
__borg_setup_common_archive_filters_options() {
|
|
local -a common_prefix_and_glob_archives_filter_options
|
|
__borg_setup_common_prefix_and_glob_archives_filter_options
|
|
typeset -ga common_archive_filters_options=(
|
|
$common_prefix_and_glob_archives_filter_options
|
|
'--sort-by=[Comma-separated list of sorting keys, default: timestamp]:KEYS:(timestamp name id)'
|
|
'(--last)--first=[consider first N archives after other filters were applied]:N: _borg_archive -n "${line[1]%%\:\:*}"'
|
|
'(--first)--last=[consider last N archives after other filters were applied]:N: _borg_archive -n "${line[1]%%\:\:*}"'
|
|
)
|
|
}
|
|
|
|
(( $+functions[__borg_setup_common_dry_run_stats_options] )) ||
|
|
__borg_setup_common_dry_run_stats_options() {
|
|
typeset -ga common_dry_run_stats_options=(
|
|
'(-n --dry-run -s --stats)'{-n,--dry-run}'[do not change anything]'
|
|
'(-n --dry-run -s --stats)'{-s,--stats}'[print statistics at end]'
|
|
# NOTE: actual messages for subcommands differ in details
|
|
)
|
|
}
|
|
|
|
(( $+functions[__borg_setup_common_create_options] )) ||
|
|
__borg_setup_common_create_options() {
|
|
local -a common_dry_run_stats_options common_exclude_options
|
|
__borg_setup_common_dry_run_stats_options
|
|
__borg_setup_common_exclude_options
|
|
typeset -ga common_create_options=(
|
|
$common_dry_run_stats_options
|
|
'--list[output verbose list of items (files, dirs, ...)]'
|
|
'--filter=[only display items with the given status characters]: :_borg_statuschars'
|
|
$common_exclude_options
|
|
'--exclude-caches[exclude directories that contain a CACHEDIR.TAG file]'
|
|
'*--exclude-if-present=[exclude directories that are tagged by containing a filesystem object with the given NAME]:NAME:_files'
|
|
'--keep-exclude-tags[if tag objects are specified with --exclude-if-present, don'\''t omit the tag objects themselves]'
|
|
'--comment=[add a comment text to the archive]:COMMENT:_borg_placeholders'
|
|
'--timestamp=[manually specify the archive creation date/time]:TIMESTAMP:_borg_timestamp'
|
|
'(-c --checkpoint-interval)'{-c,--checkpoint-interval}'=[write checkpoint every SECONDS seconds (default: 1800)]: : _borg_guard_unsigned_number "SECONDS"'
|
|
'--chunker-params=[specify the chunker parameters]: :_borg_chunker_params_examples'
|
|
'(-C --compression)'{-C,--compression}'=[select compression algorithm]: :_borg_compression'
|
|
)
|
|
}
|
|
|
|
(( $+functions[__borg_setup_common_init_options] )) ||
|
|
__borg_setup_common_init_options() {
|
|
local -a common_options
|
|
__borg_setup_common_options
|
|
typeset -ga common_init_options=(
|
|
$common_options
|
|
'--append-only[only allow appending to repository segment files]'
|
|
'--storage-quota=[override storage quota of the repository]: :_borg_quota_suffixes'
|
|
)
|
|
}
|
|
|
|
(( $+functions[_borgfs] )) ||
|
|
_borgfs() {
|
|
_borg-mount '(- :)'{-V,--version}'[show version number and exit]'
|
|
}
|
|
|
|
(( $+functions[_borg_parameters] )) ||
|
|
_borg_parameters() {
|
|
local name=$1
|
|
shift
|
|
local -i ret=1
|
|
local -a expl
|
|
|
|
case $name in
|
|
(REPO)
|
|
local BORG_REPO
|
|
unset BORG_REPO
|
|
_borg_repository && ret=0
|
|
;;
|
|
((|NEW_)PASSPHRASE)
|
|
_message -e 'passphrase' && ret=0
|
|
;;
|
|
(DISPLAY_PASSPHRASE)
|
|
_message -e 'answer to the "display the passphrase for verification" question' && ret=0
|
|
;;
|
|
(HOST_ID)
|
|
_message -e 'unique ID' && ret=0
|
|
;;
|
|
(FILES_CACHE_TTL)
|
|
_borg_guard_unsigned_number 'time to live (default: 20)' && ret=0
|
|
;;
|
|
(MOUNT_DATA_CACHE_ENTRIES)
|
|
_borg_guard_unsigned_number 'number of cached data chunks' && ret=0
|
|
;;
|
|
(PASSCOMMAND|RSH|REMOTE_PATH)
|
|
_cmdstring && ret=0
|
|
;;
|
|
(HOSTNAME_IS_UNIQUE|SHOW_SYSINFO|(UNKNOWN_UNENCRYPTED|RELOCATED)_REPO_ACCESS_IS_OK)
|
|
_description values expl 'value'
|
|
compadd "$expl[@]" yes no && ret=0
|
|
;;
|
|
((CHECK|DELETE)_I_KNOW_WHAT_I_AM_DOING)
|
|
_description values expl 'value'
|
|
compadd "$expl[@]" YES NO && ret=0
|
|
;;
|
|
(WORKAROUNDS)
|
|
_wanted workarounds expl 'workaround' _sequence compadd - basesyncfile && ret=0
|
|
;;
|
|
(KEYS_DIR)
|
|
_directories && ret=0
|
|
;;
|
|
(*)
|
|
_default && ret=0
|
|
;;
|
|
esac
|
|
|
|
return ret
|
|
}
|
|
|
|
(( $+functions[_borg_repository] )) ||
|
|
_borg_repository() {
|
|
local -a alts opts qopts
|
|
zparseopts -E -a opts S:
|
|
qopts=( ${(q)opts} )
|
|
[[ -n $BORG_REPO ]] && alts+=( "default-repository: : __borg_default_repository $qopts" )
|
|
alts+=( "cached-repositories:cached repositories:_borg_cached_repositories $qopts" )
|
|
alts+=( 'directories: :_directories -r ":/ \t\n\-"' )
|
|
alts+=( 'remote-repositories: : _borg_remote_repositories' )
|
|
_alternative $alts
|
|
}
|
|
|
|
(( $+functions[__borg_default_repository] )) ||
|
|
__borg_default_repository() {
|
|
local -a opts suf
|
|
zparseopts -E -a opts S:
|
|
(( $opts[(I)-S] )) && suf=( -S '' )
|
|
local -a default_repository=( "\:\::$BORG_REPO" )
|
|
_describe -t default-repository 'default repository' default_repository "$suf[@]"
|
|
}
|
|
|
|
(( $+functions[_borg_cached_repositories] )) ||
|
|
_borg_cached_repositories() {
|
|
local -a cached_repos
|
|
local sed_script='/^previous_location = / {
|
|
s///
|
|
# no port was given
|
|
/ssh:\/\/[^/:]+:[0-9]+/! {
|
|
# lstrip the `ssh://` prefix and add a colon before the first slash
|
|
s!ssh://([^:/]+)/(.*)!\1:/\2!
|
|
}
|
|
p
|
|
}'
|
|
local cachedir=${BORG_CACHE_DIR:-${XDG_CACHE_HOME:-${BORG_BASE_DIR:-$HOME}/.cache}/borg}
|
|
cached_repos=( ${(f)"$(_call_program -p cached-repositories sed -n -E ${(q)sed_script} \
|
|
"${(q)cachedir}/*/config(#qN.om)" 2>/dev/null)"} )
|
|
|
|
if [[ $compstate[quote] != (\'|\") ]]; then
|
|
# hide ~BORG_REPO and other scalars
|
|
local BORG_REPO
|
|
unset BORG_REPO sed_script cachedir
|
|
cached_repos=( "${(@D)cached_repos}" )
|
|
fi
|
|
|
|
compadd -Q "$@" -r ': \t\n\-' -a cached_repos
|
|
}
|
|
|
|
(( $+functions[_borg_remote_repositories] )) ||
|
|
_borg_remote_repositories() {
|
|
local -a match mbegin mend expl alts
|
|
if compset -P '(#b)ssh://[^/]##@[^/]##:([0-9]##)/'; then
|
|
_remote_files -/ -- ssh -p $match[1]
|
|
return
|
|
fi
|
|
local -i have_scheme=0
|
|
compset -P 'ssh://' && have_scheme=1
|
|
if compset -P '*:'; then
|
|
(( have_scheme )) && alts+=( 'ports: : _borg_guard_unsigned_number "port"' )
|
|
alts+=( 'remote-files:remote file: _remote_files -/ -- ssh' )
|
|
_alternative $alts
|
|
elif compset -P 1 '(#b)(*)@'; then
|
|
local user=$match[1]
|
|
_wanted -C user-at hosts expl "host for $user" \
|
|
_combination -s '[:@]' accounts users-hosts users="$user" hosts -S ':' -
|
|
elif compset -S '@*'; then
|
|
_wanted users expl "user" \
|
|
_combination -s '[:@]' accounts users-hosts users -q -
|
|
else
|
|
alts=(
|
|
'users:user:_users -S "@"'
|
|
'hosts:host:_hosts -S ":"'
|
|
)
|
|
(( ! have_scheme )) && alts+=( 'prefixes:ssh:compadd -S "" ssh://' )
|
|
_alternative $alts
|
|
fi
|
|
}
|
|
|
|
# _borg_repository_or_archive [-a] [-p]
|
|
#
|
|
# -a archive is mandatory. The suffix `::` will be added to the repository if possible.
|
|
# -p complete placeholders
|
|
(( $+functions[_borg_repository_or_archive] )) ||
|
|
_borg_repository_or_archive() {
|
|
local -A opts
|
|
zparseopts -A opts -D -E a p
|
|
|
|
if compset -P 1 '*::'; then
|
|
local qrepo=$IPREFIX[1,-3]
|
|
local -i def_repo=0
|
|
[[ -z $qrepo && -n $BORG_REPO ]] && qrepo=${(q)BORG_REPO} && def_repo=1
|
|
if [[ -n $qrepo ]]; then
|
|
|
|
if (( ! def_repo )); then
|
|
case $compstate[quote] in
|
|
(\') qrepo=${(qq)qrepo} ;;
|
|
(\") qrepo=${(qqq)${(e)qrepo}} ;;
|
|
# NOTE: currently `(e)` don't have any effect, but maybe one day zsh will stop to change the quoting method
|
|
# of double quoted parameters
|
|
esac
|
|
fi
|
|
|
|
if (( $+opts[-p] )); then
|
|
_borg_placeholder_or_archive $qrepo
|
|
else
|
|
_borg_archive $qrepo
|
|
fi
|
|
else
|
|
_message "not a borg repository: ${(Q)qrepo}"
|
|
return 1
|
|
fi
|
|
else
|
|
local -a suf
|
|
if ! compset -S '::*'; then
|
|
if (( $+opts[-a] )) || zstyle -T ":completion:${curcontext}:repositories" repository-suffix; then
|
|
suf=( -S '::' )
|
|
fi
|
|
local oqrepo="$PREFIX$SUFFIX"
|
|
local qrepo=$oqrepo
|
|
[[ $compstate[quote] != (\'|\") ]] && qrepo=${(Q)qrepo}
|
|
if __borg_is_borg_repo $qrepo; then
|
|
qrepo=${oqrepo%%/}
|
|
[[ -z $SUFFIX ]] && PREFIX=${PREFIX%%/} || SUFFIX=${SUFFIX%%/}
|
|
compadd -S '::' -r ':/ \t\n\-' -Q -- $qrepo
|
|
return
|
|
fi
|
|
fi
|
|
_borg_repository "$suf[@]"
|
|
fi
|
|
}
|
|
|
|
# _borg_archive [-F] [-n] [qrepo]
|
|
#
|
|
# -F don't apply archive filter options on the command line
|
|
# -n reverse order, disable matchers and don't do menu completion/selection
|
|
(( $+functions[_borg_archive] )) ||
|
|
_borg_archive() {
|
|
local -A opts
|
|
zparseopts -A opts -D -E F n
|
|
|
|
local qrepo=$1
|
|
|
|
if [[ -z $qrepo ]]; then
|
|
if [[ -n $BORG_REPO ]]; then
|
|
qrepo=${(q)BORG_REPO}
|
|
else
|
|
_message 'no repository specified'
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
local -i ret=1
|
|
_tags archives
|
|
while _tags; do
|
|
|
|
if _requested archives; then
|
|
|
|
local -a expl disp archive_filters
|
|
local -i reversed_order=1
|
|
|
|
if (( ! $+opts[-F] )); then
|
|
local -a archive_filter_options=( -P --prefix -a --glob-archives --first --last --sort-by ) tmp
|
|
local k
|
|
for k in $archive_filter_options; do
|
|
if [[ -n $opt_args[$k] ]]; then
|
|
IFS=: read -A tmp <<<$opt_args[$k]
|
|
archive_filters+=( $k=${^tmp:#} )
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if (( $+opts[-n] )); then
|
|
__borg_skip_pattern_matching || return 1
|
|
|
|
disp+=( -U )
|
|
|
|
compstate[insert]=''
|
|
compstate[list]='list force'
|
|
|
|
reversed_order=0
|
|
fi
|
|
|
|
local -a asort
|
|
zstyle -a ":completion:${curcontext}:archives" archive-sort asort
|
|
if (( $asort[(I)inverse] )); then
|
|
(( reversed_order = ! reversed_order ))
|
|
fi
|
|
local -a sort_by=( --sort-by=${(M)^asort:#(timestamp|name|id)} )
|
|
# NOTE: in case of option repetition, the later one takes precedence
|
|
|
|
if (( ! $+__borg_archives_need_update )); then
|
|
comppostfuncs+=( __borg_unset_archives_need_update )
|
|
typeset -gHi __borg_archives_need_update=1
|
|
if (( ! $#archive_filters && ! $+opts[-n] )); then
|
|
local erepo
|
|
[[ -n $1 ]] && __borg_expand_path ${(Q)qrepo} erepo
|
|
local -a newest_file=( $erepo/(hints|index|integrity).<1->(#qN.om[1]) )
|
|
if [[ -n $newest_file ]]; then
|
|
if zmodload -F zsh/stat b:zstat 2>/dev/null; then
|
|
local -a stats
|
|
zstat -A stats +mtime $newest_file
|
|
local -i mtime=$stats[1]
|
|
if [[ $__borg_prev_repo == $erepo
|
|
&& __borg_prev_mtime -ge mtime
|
|
&& $__borg_prev_order == $reversed_order
|
|
&& $__borg_prev_sort_by == $sort_by ]]
|
|
then
|
|
__borg_archives_need_update=0
|
|
else
|
|
typeset -gH __borg_prev_repo=$erepo
|
|
typeset -gHi __borg_prev_mtime=mtime __borg_prev_order=reversed_order
|
|
typeset -gHa __borg_prev_sort_by=( $sort_by )
|
|
fi
|
|
fi
|
|
fi
|
|
else
|
|
unset __borg_prev_{repo,mtime,order,sort_by}
|
|
comppostfuncs+=( __borg_unset_archives )
|
|
fi
|
|
fi
|
|
|
|
if zstyle -t ":completion:${curcontext}:archives" verbose; then
|
|
if (( __borg_archives_need_update || ! $+__borg_archive_names || ! $+__borg_archive_descriptions )); then
|
|
__borg_archives_need_update=0
|
|
typeset -gHa __borg_archive_names=() __borg_archive_descriptions=()
|
|
local fmt descfmt name desc
|
|
zstyle -s ":completion:${curcontext}:archives" archive-description-format descfmt ||
|
|
descfmt='{archive:<36} {time} [{id}]'
|
|
fmt="{barchive}{NUL}$descfmt{NUL}"
|
|
_call_program -p archive-descriptions \
|
|
${(q)__borg_command:-borg} list --format=${(q)fmt} ${(q)sort_by} $archive_filters $qrepo 2>/dev/null |
|
|
while IFS= read -r -d $'\0' name && IFS= read -r -d $'\0' descr; do
|
|
__borg_archive_names[1,0]=( $name )
|
|
__borg_archive_descriptions[1,0]=( "$descr" )
|
|
done
|
|
(( $pipestatus[1] )) && {
|
|
_message "couldn't list repository: ${(Q)qrepo}"
|
|
unset __borg_prev_{repo,mtime,order,sort_by}
|
|
return 1
|
|
}
|
|
(( ! reversed_order )) &&
|
|
__borg_archive_names=( "${(@aO)__borg_archive_names}" ) &&
|
|
__borg_archive_descriptions=( "${(@aO)__borg_archive_descriptions}" )
|
|
fi
|
|
disp+=( -ld __borg_archive_descriptions )
|
|
elif (( __borg_archives_need_update || ! $+__borg_archive_names )); then
|
|
__borg_archives_need_update=0
|
|
typeset -gHa __borg_archive_names=()
|
|
local fmt='{barchive}{NUL}'
|
|
__borg_archive_names=( ${(@0aO)"$(_call_program -p archives \
|
|
${(q)__borg_command:-borg} list --format=${(q)fmt} ${(q)sort_by} $archive_filters $qrepo 2>/dev/null)"} )
|
|
(( $pipestatus[1] )) && {
|
|
_message "couldn't list repository: ${(Q)qrepo}"
|
|
unset __borg_prev_{repo,mtime,order,sort_by}
|
|
return 1
|
|
}
|
|
(( ! reversed_order )) &&
|
|
__borg_archive_names=( "${(@aO)__borg_archive_names}" )
|
|
fi
|
|
|
|
_all_labels archives expl 'ARCHIVE' compadd "$disp[@]" -a __borg_archive_names && ret=0
|
|
|
|
fi
|
|
|
|
(( ret )) || return 0
|
|
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
(( $+functions[__borg_unset_archives] )) ||
|
|
__borg_unset_archives() {
|
|
unset __borg_archive_names __borg_archive_descriptions
|
|
}
|
|
|
|
(( $+functions[__borg_unset_archives_need_update] )) ||
|
|
__borg_unset_archives_need_update() {
|
|
unset __borg_archives_need_update
|
|
}
|
|
|
|
(( $+functions[__borg_is_borg_repo] )) ||
|
|
__borg_is_borg_repo() {
|
|
local repo=$1
|
|
__borg_expand_path $repo repo
|
|
if [[ -d $repo && -d $repo/data ]]; then
|
|
local -a files=( $repo/(hints|index|integrity).<1->(#qN.) )
|
|
(( $#files >= 3 )) && return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
(( $+functions[__borg_expand_path] )) ||
|
|
__borg_expand_path() {
|
|
local _path=$1
|
|
local -a match mbegin mend
|
|
if [[ $_path == (#b)(\~[^/]#)(|/*) ]]; then
|
|
local etilde
|
|
etilde=$~match[1] 2>/dev/null
|
|
_path="$etilde$match[2]"
|
|
fi
|
|
_path=${(e)_path//\\\\/\\\\\\\\}
|
|
eval typeset -g ${2:-REPLY}=\$_path
|
|
}
|
|
|
|
(( $+functions[_borg_placeholder_or_archive] )) ||
|
|
_borg_placeholder_or_archive() {
|
|
local qrepo=$1
|
|
shift
|
|
_alternative \
|
|
'placeholders: :_borg_placeholders' \
|
|
"archives: : _borg_archive ${(q)qrepo}"
|
|
}
|
|
|
|
(( $+functions[_borg_placeholders] )) ||
|
|
_borg_placeholders() {
|
|
local -a placeholders=(
|
|
'hostname:The (short) hostname of the machine.'
|
|
'fqdn:The full name of the machine.'
|
|
'reverse-fqdn:The full name of the machine in reverse domain name notation.'
|
|
'now:The current local date and time, by default in ISO-8601 format. You can also supply your own format string, e.g. {now:%Y-%m-%d_%H:%M:%S}'
|
|
'utcnow:The current UTC date and time, by default in ISO-8601 format. You can also supply your own format string, e.g. {utcnow:%Y-%m-%d_%H:%M:%S}'
|
|
'user:The user name (or UID, if no name is available) of the user running borg.'
|
|
'pid:The current process ID.'
|
|
'borgversion:The version of borg, e.g.: 1.0.8rc1'
|
|
'borgmajor:The version of borg, only the major version, e.g.: 1'
|
|
'borgminor:The version of borg, only major and minor version, e.g.: 1.0'
|
|
'borgpatch:The version of borg, only major, minor and patch version, e.g.: 1.0.8'
|
|
)
|
|
__borg_complete_keys _describe -t placeholders 'placeholder' placeholders '"$copts[@]"'
|
|
}
|
|
|
|
(( $+functions[_borg_format_keys] )) ||
|
|
_borg_format_keys() {
|
|
local repo_or_arch=${(Q)1}
|
|
|
|
local -a keys=( NEWLINE NL NUL SPACE TAB CR LF )
|
|
local -a repository_keys=( archive name barchive comment bcomment id start time end hostname username )
|
|
local -a archive_keys=( type mode uid gid user group path bpath source linktarget flags size csize dsize dcsize
|
|
num_chunks unique_chunks mtime ctime atime isomtime isoctime isoatime blake2b blake2s md5 sha1 sha224 sha256 sha384
|
|
sha3_224 sha3_256 sha3_384 sha3_512 sha512 shake_128 shake_256 archiveid archivename extra health )
|
|
|
|
local akeys rkeys
|
|
akeys='archive-keys:archive keys:compadd -a archive_keys'
|
|
rkeys='repository-keys:repository keys:compadd -a repository_keys'
|
|
local -a alts=( 'keys:keys:compadd -a keys' )
|
|
if [[ $repo_or_arch == *::?* ]]; then
|
|
alts+=( $akeys )
|
|
elif [[ -n $repo_or_arch ]]; then
|
|
alts+=( $rkeys )
|
|
else
|
|
alts+=( $rkeys $akeys )
|
|
fi
|
|
|
|
__borg_complete_keys _alternative -O copts ${(q)alts}
|
|
}
|
|
|
|
(( $+functions[__borg_complete_keys] )) ||
|
|
__borg_complete_keys() {
|
|
compset -P '*[^A-Za-z]##'
|
|
compset -S '[^A-Za-z]##*'
|
|
|
|
[[ -n $ISUFFIX ]] && compstate[to_end]=''
|
|
# NOTE: `[[ -n $ISUFFIX ]]` is a workarond for a bug that causes cursor movement to the right further than it should
|
|
# NOTE: the _oldlist completer doesn't respect compstate[to_end]=''
|
|
|
|
local ipref suf
|
|
if [[ $IPREFIX[-1] != '{' ]]; then
|
|
ipref='{'
|
|
[[ $compstate[quote] != (\'|\") ]] && ipref='\{'
|
|
fi
|
|
if [[ $ISUFFIX[1] != (|\\)\} ]]; then
|
|
suf='}'
|
|
[[ $compstate[quote] != (\'|\") ]] && suf='\}'
|
|
fi
|
|
|
|
local -a copts=( -i "$ipref" -S "$suf" )
|
|
eval "$@"
|
|
}
|
|
|
|
# _borg_style_selector_or_archive_files [-e] [-p] archive default_style_selector
|
|
#
|
|
# -e apply exclusion options on the command line
|
|
# -p complete `--pattern`
|
|
# -f complete files rather than borg paths
|
|
(( $+functions[_borg_style_selector_or_archive_files] )) ||
|
|
_borg_style_selector_or_archive_files() {
|
|
local -A opts
|
|
zparseopts -A opts -D -E e p f
|
|
|
|
local arch=$1 default_style_selector=$2
|
|
shift 2
|
|
|
|
local -a match mbegin mend expl tags=( style-selectors archive-files ) ss_suf=( -S ':' -r ':' )
|
|
(( $+opts[-f] )) && tags=( style-selectors files )
|
|
local -i ret=1
|
|
|
|
if (( $+opts[-p] )); then
|
|
if ! compset -P '(#b)([RP\+\-\!])'; then
|
|
local -a pattern_rules=(
|
|
'P:pattern style'
|
|
'R:root path'
|
|
'+:include'
|
|
'-:exclude'
|
|
'!:exclude non-recurse'
|
|
)
|
|
_describe -t pattern-rules 'pattern rule' pattern_rules -S ''
|
|
return
|
|
else
|
|
if [[ $compstate[quote] == (\'|\") ]]; then
|
|
compset -P ' #'
|
|
else
|
|
compset -P '(\\ )#'
|
|
fi
|
|
if [[ $match[1] == 'R' ]]; then
|
|
default_style_selector='pp'
|
|
elif [[ $match[1] == 'P' ]]; then
|
|
tags=( style-selectors )
|
|
ss_suf=()
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
_tags $tags
|
|
while _tags; do
|
|
if _requested style-selectors; then
|
|
_all_labels style-selectors expl 'style selector' \
|
|
__borg_style_selectors $default_style_selector "$ss_suf[@]" - && ret=0
|
|
fi
|
|
if _requested archive-files; then
|
|
_all_labels archive-files expl 'PATTERN' \
|
|
__borg_archive_files ${(k)opts} "$arch" $default_style_selector - && ret=0
|
|
fi
|
|
if _requested files; then
|
|
local -a borg_paths=( ${(Q)${(e)${~@}}} )
|
|
_all_labels files expl 'PATH' \
|
|
__borg_pattern_files ${(k)opts} borg_paths - && ret=0
|
|
fi
|
|
(( ret )) || return 0
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
(( $+functions[__borg_style_selectors] )) ||
|
|
__borg_style_selectors() {
|
|
local default_style_selector=$1 path_style_selector
|
|
shift
|
|
zstyle -s ":completion:${curcontext}:archive-files" path-style-selector path_style_selector ||
|
|
path_style_selector='fm'
|
|
local -a disp
|
|
local -A style_selectors
|
|
__borg_setup_style_selectors
|
|
if zstyle -T ":completion:${curcontext}:style-selectors" verbose; then
|
|
local -a style_selector_descriptions extra
|
|
local k v sep
|
|
for k v in ${(kv)style_selectors}; do
|
|
extra=()
|
|
[[ $k == $default_style_selector ]] && extra+=( 'default' )
|
|
[[ $k == $path_style_selector ]] && __borg_choose_path_or_pattern "" "$default_style_selector" &&
|
|
extra+=( 'path' )
|
|
(( $#extra )) && v+=" (${(j:, :)extra})"
|
|
style_selector_descriptions+=( "${${k//\\/\\\\}//:/\\:}:$v" )
|
|
done
|
|
zstyle -s ":completion:${curcontext}:style-selectors" list-separator sep || sep=--
|
|
zformat -a style_selector_descriptions " $sep " $style_selector_descriptions
|
|
disp=( -ld style_selector_descriptions )
|
|
fi
|
|
compadd "$disp[@]" "$@" -k style_selectors
|
|
}
|
|
|
|
(( $+functions[__borg_archive_files] )) ||
|
|
__borg_archive_files() {
|
|
local -A opts
|
|
zparseopts -A opts -D e p
|
|
|
|
local arch=$1 default_style_selector=$2
|
|
shift 2
|
|
|
|
if [[ -z $arch || $arch != *::?* ]]; then
|
|
_message 'no archive specified'
|
|
return 1
|
|
fi
|
|
|
|
local -a qargs tmp disp pref match mbegin mend archive_files descs
|
|
local -A style_selectors
|
|
local k cword fmt descfmt style_selector path_style_selector name descr
|
|
|
|
# take into account exclude options on the command line
|
|
if (( $+opts[-e] )); then
|
|
local -a exclude_options=( -e --exclude --exclude-from --pattern --pattern-from )
|
|
local -a excludes
|
|
for k in $exclude_options; do
|
|
if [[ -n $opt_args[$k] ]]; then
|
|
IFS=: read -A tmp <<<$opt_args[$k]
|
|
excludes+=( $k="${^tmp[@]}" )
|
|
fi
|
|
done
|
|
[[ -n $excludes ]] && qargs+=( "$excludes[@]" )
|
|
fi
|
|
|
|
(( $_matcher_num > 1 )) && return 1
|
|
__borg_skip_pattern_matching || return 1
|
|
|
|
cword="$PREFIX$SUFFIX"
|
|
[[ $compstate[quote] != (\'|\") ]] && cword=${(Q)cword}
|
|
|
|
[[ -z $cword ]] && return 1
|
|
|
|
if zstyle -t ":completion:${curcontext}:archive-files" verbose; then
|
|
zstyle -s ":completion:${curcontext}:archive-files" file-description-format descfmt ||
|
|
descfmt='{mode} {user:6} {group:6} {size:8d} {mtime} {path}{extra}'
|
|
fmt="{bpath}{NUL}$descfmt{NUL}"
|
|
else
|
|
fmt='{bpath}{NUL}'
|
|
fi
|
|
qargs+=( --format=${(q)fmt} )
|
|
|
|
qargs+=( $arch )
|
|
|
|
__borg_setup_style_selectors
|
|
[[ $cword == (#b)(${~${(j:|:)${(kb)style_selectors}}}):* ]] && style_selector=$match[1]
|
|
|
|
local -i path_expected=0
|
|
__borg_choose_path_or_pattern "$style_selector" $default_style_selector $cword && path_expected=1
|
|
|
|
if [[ -n $cword ]]; then
|
|
if (( path_expected )); then
|
|
[[ -n $style_selector ]] && compset -P "$style_selector:" && pref=( -P "$style_selector:" )
|
|
cword="$PREFIX$SUFFIX"
|
|
[[ $compstate[quote] != (\'|\") ]] && cword=${(Q)cword}
|
|
zstyle -s ":completion:${curcontext}:archive-files" path-style-selector path_style_selector ||
|
|
path_style_selector='fm'
|
|
cword="$path_style_selector:$cword"
|
|
else
|
|
[[ -z $style_selector ]] && cword="$default_style_selector:$cword"
|
|
fi
|
|
qargs+=( ${(q)cword} )
|
|
fi
|
|
|
|
if zstyle -t ":completion:${curcontext}:archive-files" verbose; then
|
|
_call_program -p archive-file-descriptions ${(q)__borg_command:-borg} list $qargs 2>/dev/null |
|
|
while IFS= read -r -d $'\0' name && IFS= read -r -d $'\0' descr; do
|
|
archive_files+=( $name )
|
|
descs+=( $descr )
|
|
done
|
|
(( $pipestatus[1] )) && { _message "couldn't list archive: ${(Q)arch}"; return 1 }
|
|
disp=( -ld descs )
|
|
else
|
|
archive_files=( ${(0)"$(_call_program -p archive-files ${(q)__borg_command:-borg} list $qargs 2>/dev/null)"} )
|
|
(( $pipestatus[1] )) && { _message "couldn't list archive: ${(Q)arch}"; return 1 }
|
|
fi
|
|
|
|
if (( $#archive_files )); then
|
|
if (( path_expected )); then
|
|
compstate[insert]='automenu'
|
|
else
|
|
compstate[insert]=''
|
|
compstate[list]='list force'
|
|
fi
|
|
fi
|
|
|
|
compadd "$pref[@]" -U "$disp[@]" "$@" -a archive_files
|
|
}
|
|
|
|
(( $+functions[__borg_choose_path_or_pattern] )) ||
|
|
__borg_choose_path_or_pattern() {
|
|
local ss=$1 defss=$2 cword=$3
|
|
shift 2
|
|
[[ $ss == (pp|pf) || ( -z $ss && $defss == (pp|pf) ) ]]
|
|
}
|
|
|
|
# transform borg exclude patterns into zsh ignore patterns and then complete files
|
|
(( $+functions[__borg_pattern_files] )) ||
|
|
__borg_pattern_files() {
|
|
local -A opts
|
|
zparseopts -A opts -D -E e p f
|
|
|
|
local paths_varname=$1
|
|
shift
|
|
|
|
local -a args
|
|
local -A style_selectors
|
|
__borg_setup_style_selectors
|
|
local pr_pat='[RP\+\-\!]' ss_pat="(${(j:|:)${(@kb)style_selectors}}):"
|
|
local prs_pat="$pr_pat #"
|
|
|
|
if (( $+opts[-e] )); then
|
|
local -a borg_excludes exclude_options=( -e --exclude --pattern ) tmp
|
|
local k cword
|
|
local -i i
|
|
for k in $exclude_options; do
|
|
if [[ -n $opt_args[$k] ]]; then
|
|
IFS=: read -A tmp <<<$opt_args[$k]
|
|
tmp=( ${(Q)tmp} )
|
|
# lstrip style selectors and pattern rules
|
|
[[ $+opts[-p] -gt 0 || $k == --pattern ]] && tmp=( ${tmp#$~prs_pat} )
|
|
tmp=( ${tmp#$~ss_pat} )
|
|
|
|
# don't take into account the word under the cursor
|
|
cword="$PREFIX$SUFFIX"
|
|
[[ $compstate[quote] != (\'|\") ]] && cword=${(Q)cword}
|
|
[[ $+opts[-p] -gt 0 || $k == --pattern ]] && cword=${cword#$~prs_pat}
|
|
cword=${cword#$~ss_pat}
|
|
i=$tmp[(I)$cword]
|
|
(( i )) && tmp=( "${(@)tmp[1,i-1]}" "${(@)tmp[i+1,-1]}" )
|
|
|
|
borg_excludes+=( "$tmp[@]" )
|
|
fi
|
|
done
|
|
[[ -n $borg_excludes ]] && args+=( -F borg_excludes )
|
|
fi
|
|
|
|
[[ -n ${(P)paths_varname} ]] && args+=( -W $paths_varname )
|
|
|
|
args+=( "$@" )
|
|
|
|
# lstrip style selectors and pattern rules
|
|
if (( $+opts[-p] )); then
|
|
if [[ $compstate[quote] != (\'|\") ]]; then
|
|
compset -P $pr_pat
|
|
compset -P '(\\ )#'
|
|
else
|
|
compset -P $prs_pat
|
|
fi
|
|
fi
|
|
compset -P $ss_pat
|
|
|
|
compstate[insert]=''
|
|
compstate[list]='list force'
|
|
|
|
_path_files "$args[@]"
|
|
}
|
|
|
|
(( $+functions[__borg_setup_style_selectors] )) ||
|
|
__borg_setup_style_selectors() {
|
|
typeset -gA style_selectors=(
|
|
fm 'Fnmatch'
|
|
sh 'Shell-style patterns'
|
|
re 'Regular expressions'
|
|
pp 'Path prefix'
|
|
pf 'Path full-match'
|
|
)
|
|
}
|
|
|
|
(( $+functions[__borg_skip_pattern_matching] )) ||
|
|
__borg_skip_pattern_matching() {
|
|
# unset glob_complete
|
|
[[ $compstate[pattern_match] == '*' ]] && compstate[pattern_match]=''
|
|
# skip the _match completer
|
|
[[ -n $compstate[pattern_match] ]] && return 1
|
|
return 0
|
|
}
|
|
|
|
(( $+functions[_borg_config] )) ||
|
|
_borg_config() {
|
|
local qrepo=$1
|
|
shift
|
|
|
|
if (( ! $+__borg_config_sect )); then
|
|
comppostfuncs+=( __borg_unset_config )
|
|
typeset -gH __borg_config_sect=
|
|
typeset -gHa __borg_config_keys=()
|
|
local sect line
|
|
local -a match mbegin mend
|
|
_call_program -p keys ${(q)__borg_command:-borg} config --list $qrepo 2>/dev/null | {
|
|
IFS= read -r sect
|
|
sect=${${sect#\[}%\]}
|
|
__borg_config_sect=$sect
|
|
while IFS= read -r line && [[ $line == (#b)(*)\ =\ (*) ]]; do
|
|
__borg_config_keys+=( "${${match[1]//\\/\\\\}//:/\\:}:(current: $match[2])" )
|
|
done
|
|
}
|
|
fi
|
|
|
|
local -a alts=( 'keys:key: _describe -t keys "key" __borg_config_keys' )
|
|
compset -P "${__borg_config_sect}." || alts+=( 'sections:section:compadd -S "." $__borg_config_sect' )
|
|
_alternative $alts
|
|
}
|
|
|
|
(( $+functions[__borg_unset_config] )) ||
|
|
__borg_unset_config() {
|
|
unset __borg_config_sect __borg_config_keys
|
|
}
|
|
|
|
# A simple prefix-oriented completion function for compressors. Can be improved by supporting the suffix.
|
|
(( $+functions[_borg_compression] )) ||
|
|
_borg_compression() {
|
|
local -a nolvl=(
|
|
'none:do not compress'
|
|
'lz4:very high speed, very low compression'
|
|
)
|
|
local -a havelvl=(
|
|
'zstd:("zstandart")'
|
|
'zlib:("gz") medium speed, medium compression'
|
|
'lzma:("xz") low speed, high compression'
|
|
)
|
|
local -a auto=(
|
|
'auto:compress compressible, otherwise "none"'
|
|
)
|
|
local -a match mbegin mend
|
|
# NOTE: Zsh's `-prefix` condition is confused by the leading parenthesis in the pattern.
|
|
# Fortunately, we simply need to show a message.
|
|
if compset -P '(#b)(|auto,)(zstd|zlib|lzma),'; then
|
|
local -i from to def
|
|
case $match[2] in
|
|
(zstd) from=1 to=22 def=3 ;;
|
|
(zlib|lzma) from=0 to=9 def=6 ;;
|
|
esac
|
|
_message -e "compression level (from $from to $to, default: $def)"
|
|
elif compset -P 'auto,'; then
|
|
_describe -t compression 'compression' nolvl -- havelvl -qS,
|
|
else
|
|
_describe -t compression 'compression' nolvl -- havelvl -qS, -- auto -S,
|
|
fi
|
|
}
|
|
|
|
(( $+functions[_borg_chunker_params] )) ||
|
|
_borg_chunker_params() {
|
|
if compset -P 'buzhash,'; then
|
|
if compset -P '*,*,*,'; then
|
|
_message -e 'HASH_WINDOW_SIZE'
|
|
elif compset -P '*,*,'; then
|
|
_message -e 'HASH_MASK_BITS (statistical medium chunk size ~= 2^HASH_MASK_BITS B)'
|
|
elif compset -P '*,'; then
|
|
_message -e 'CHUNK_MAX_EXP (maximum chunk size = 2^CHUNK_MAX_EXP B)'
|
|
else
|
|
_message -e 'CHUNK_MIN_EXP (minimum chunk size = 2^CHUNK_MIN_EXP B)'
|
|
fi
|
|
elif compset -P 'fixed,'; then
|
|
if compset -P '*,'; then
|
|
_message -e 'HEADER_SIZE (B)'
|
|
else
|
|
_message -e 'BLOCK_SIZE (B)'
|
|
fi
|
|
else
|
|
local -a algorithms=(
|
|
'fixed:a simple, low cpu overhead, fixed blocksize chunker, optionally supporting a header block of different size'
|
|
'buzhash:variable, content-defined blocksize, uses a rolling hash computed by the Buzhash algorithm'
|
|
)
|
|
_describe -t algorithm 'ALGO' algorithms -S ,
|
|
fi
|
|
}
|
|
|
|
(( $+functions[_borg_chunker_params_examples] )) ||
|
|
_borg_chunker_params_examples() {
|
|
local -a params=(
|
|
'default:buzhash,19,23,21,4095'
|
|
'buzhash,19,23,21,4095:small amount of chunks (default)'
|
|
'buzhash,10,23,16,4095:big amount of chunks'
|
|
)
|
|
params=( ${(q)params} )
|
|
_alternative \
|
|
'chunker-params: :_borg_chunker_params' \
|
|
"chunker-params-examples:chunker params examples:(($params))"
|
|
}
|
|
|
|
(( $+functions[_borg_statuschars] )) ||
|
|
_borg_statuschars() {
|
|
_values -s '' 'STATUSCHARS' \
|
|
'A[regular file, added]' \
|
|
'M[regular file, modified]' \
|
|
'U[regular file, unchanged]' \
|
|
'C[regular file, it changed while we backed it up]' \
|
|
'E[regular file, an error happened while accessing/reading this file]' \
|
|
'd[directory]' \
|
|
'b[block device]' \
|
|
'c[char device]' \
|
|
'h[regular file, hardlink (to already seen inodes)]' \
|
|
's[symlink]' \
|
|
'f[fifo]' \
|
|
'i[backup data was read from standard input (stdin)]' \
|
|
'-[dry run, item was not backed up]' \
|
|
'x[excluded, item was not backed up]' \
|
|
'?[missing status code]'
|
|
}
|
|
|
|
(( $+functions[_borg_quota_suffixes] )) ||
|
|
_borg_quota_suffixes() {
|
|
if compset -P '[0-9]##'; then
|
|
local -a suffixes=(
|
|
'K:10 ** 3 bytes'
|
|
'M:10 ** 6 bytes'
|
|
'G:10 ** 9 bytes'
|
|
'T:10 ** 12 bytes'
|
|
'P:10 ** 15 bytes'
|
|
)
|
|
# NOTE: tag `suffixes` is already in use (file extensions)
|
|
_describe -t multiplier 'suffix' suffixes
|
|
else
|
|
_message -e 'QUOTA'
|
|
fi
|
|
}
|
|
|
|
(( $+functions[_borg_timestamp] )) ||
|
|
_borg_timestamp() {
|
|
_alternative \
|
|
"dates:TIMESTAMP: _dates -f '%FT%T'" \
|
|
'files:reference:_files'
|
|
}
|
|
|
|
(( $+functions[_borg_guard_unsigned_number] )) ||
|
|
_borg_guard_unsigned_number() {
|
|
local -A opts
|
|
zparseopts -K -D -A opts M+: J+: V+: 1 2 o+: n F: x+: X+:
|
|
_guard '[0-9]#' ${1:-number}
|
|
}
|
|
|
|
_borg() {
|
|
local -a match mbegin mend line state
|
|
local curcontext="$curcontext" state_descr
|
|
typeset -A opt_args
|
|
local -i ret=1
|
|
|
|
if [[ $service == 'borg' ]]; then
|
|
local __borg_command=$words[1]
|
|
|
|
local -a common_options
|
|
__borg_setup_common_options
|
|
|
|
_arguments -s -w -C : \
|
|
'(- :)'{-V,--version}'[show version number and exit]' \
|
|
$common_options \
|
|
'(-): :->command' \
|
|
'(-)*:: :->option-or-argument' && return
|
|
|
|
case $state in
|
|
(command)
|
|
_borg_commands && ret=0
|
|
;;
|
|
(option-or-argument)
|
|
curcontext="${curcontext%:*:*}:borg-$words[1]:"
|
|
|
|
if ! _call_function ret _borg-$words[1]; then
|
|
_default && ret=0
|
|
fi
|
|
;;
|
|
esac
|
|
elif [[ $service == (#b)-value-,BORG_(*),-default- ]]; then
|
|
_borg_parameters $match[1] && ret=0
|
|
elif ! _call_function ret _$service; then
|
|
_default && ret=0
|
|
fi
|
|
|
|
return ret
|
|
}
|
|
|
|
_borg "$@"
|