diff --git a/scripts/shell_completions/zsh/_borg b/scripts/shell_completions/zsh/_borg index fc9035a4a..a893da7bb 100644 --- a/scripts/shell_completions/zsh/_borg +++ b/scripts/shell_completions/zsh/_borg @@ -1,448 +1,1609 @@ -#compdef borg +#compdef borg borgfs -P -value-,BORG_*,-default- -# ------- +# Zsh completion for Borg Backup 1.2.0a8 (2020-04-21). # -# To Install: +# Recommended _borg specific settings: # -# Copy this file to /usr/[local]/share/zsh/site-functions/ +# 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' # -# ------- -# borgbackup ZSH completion -# Kevin Gravier 2017 +# Custom styles: # -# The MIT License (MIT) +# 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 `::` suffix to a repository. +# Default: `true`. -# Copyright (c) 2017 Kevin +(( $+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_ +} -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: +(( $+functions[_borg-benchmark] )) || +_borg-benchmark() { + local -a common_options + __borg_setup_common_options -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. + _arguments -s -w -S : \ + $common_options \ + ':type:(crud)' \ + ': :_borg_repository' \ + ':PATH:_files' +} -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. +(( $+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 + __borg_setup_common_options + __borg_setup_common_create_options + + _arguments -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' \ + '--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' \ + '*:PATH:_files' +} + +(( $+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) + 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]' \ + '--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 + + _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 --compression]:params:((if-different always never\:\(default\)))' \ + $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 and remote, 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|RECREATE)_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 + ;; + (*) + _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(N.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}:" 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}:" 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}:" 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 md5 sha1 sha224 sha256 sha384 sha512 + xxh64 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 style_selectors + __borg_setup_style_selectors + if zstyle -T ":completion:${curcontext}:" 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}:" 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 style_selectors match mbegin mend archive_files descs + 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}:" verbose; then + zstyle -s ":completion:${curcontext}:" 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}:" 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}:" 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 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() { - typeset -A opt_args - local -a borg_possible_commands borg_possible_key_commands commands keyCommands borg_common_options - local command keyCommand item subItem + local -a match mbegin mend line state + local curcontext="$curcontext" state_descr + typeset -A opt_args + local -i ret=1 - borg_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}'[work on log level INFO]' - --debug'[work on log level DEBUG]' - --default-topic'[enable TOPIC debugging (can be specified multiple times).]:TOPIC' - {-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).]:SECONDS' - --show-version'[show/log the borg version]' - --show-rc'[show/log the return code (rc)]' - --umask'[set umask to M (local and remote, default: 0077)]:umask' - --remote-path'[set remote path to executable (default: "borg")]:_files' - --remote-ratelimit'[set remote network upload rate limit in kiByte/s (default: 0=unlimited)]: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.]:_files' - --rsh'[use COMMAND instead of ssh]:COMMAND') + if [[ $service == 'borg' ]]; then + local __borg_command=$words[1] - borg_possible_commands=(init create extract check rename list diff delete prune compact info mount umount key upgrade recreate export-tar serve config with-lock break-lock benchmark help) - borg_possible_key_commands=(change-passphrase import export) - command="" - keyCommand="" + local -a common_options + __borg_setup_common_options - for item in $words; do - (( ${borg_possible_commands[(I)$item]} )) && command=$item - (( ${borg_possible_key_commands[(I)$item]} )) && keyCommand=$item - done + _arguments -s -w -C : \ + '(- :)'{-V,--version}'[show version number and exit]' \ + $common_options \ + '(-): :->command' \ + '(-)*:: :->option-or-argument' && return - case $command in - (init) - _arguments \ - '2:repo:_files'\ - {-e,--encryption}'[select encryption key mode]:mode:(none keyfile keyfile-blake2 repokey repokey-blake2 authenticated authenticated-blake2)'\ - --append-only'[only allow appending to repository segment files]'\ - --storage-quota'[Override storage quota of the repository]:QUOTA'\ - --make-parent-dirs'[create parent directories]'\ - $borg_common_options - ;; - (create) - _arguments \ - '2:archives:__borg_archive'\ - '3:path:_files'\ - {-n,--dry-run}'[do not create a backup archive]'\ - {-s,--stats}'[print statistics for the created archive]'\ - --list'[output verbose list of items (files, dirs, ...)]'\ - --filter'[only display items with the given status characters]:STATUSCHARS'\ - --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]:NAME'\ - {-e,--exclude}'[exclude paths matching PATTERN]:PATTERN'\ - --exclude-from'[read exclude patterns from EXCLUDEFILE, one per line]:_files'\ - --pattern'[experimental: include/exclude paths matching PATTERN]:PATTERN'\ - --patterns-from'[experimental: read include/exclude patterns from PATTERNFILE, one per line]:_files'\ - --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'\ - --keep-exclude-tags'[if tag objects are specified with --exclude-if-present, don’t omit the tag objects themselves]'\ - {-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]'\ - --nobirthtime'[do not store birthtime (creation date) into archive]'\ - --nobsdflags'[do not read and store bsdflags (e.g. NODUMP, IMMUTABLE) into archive]'\ - --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.]'\ - --comment'[add a comment text to the archive]:COMMENT'\ - --timestamp'[manually specify the archive creation date/time]:TIMESTAMP'\ - --timestamp'[manually specify the archive creation date/time by a reference file/directory]:_files'\ - {-c,--checkpoint-interval}'[write checkpoint every SECONDS seconds]:SECONDS'\ - --chunker-params'[specify the chunker parameters]:PARAMS'\ - {-C,--compression}'[select compression algorithm]:COMPRESSION'\ - $borg_common_options - ;; - (extract) - _arguments \ - '2:archives:__borg_archive'\ - '3:path:_files'\ - --list'[output verbose list of items (files, dirs, ...)]'\ - {-n,--dry-run}'[do not actually change any files]'\ - --numeric-owner'[only obey numeric user and group identifiers]'\ - --nobsdflags'[do not extract/set bsdflags (e.g. NODUMP, IMMUTABLE)]'\ - --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]'\ - {-e,--exclude}'[exclude paths matching PATTERN]:PATTERN'\ - --exclude-from'[read exclude patterns from EXCLUDEFILE, one per line]:_files'\ - --pattern'[experimental: include/exclude paths matching PATTERN]:PATTERN'\ - --patterns-from'[experimental: read include/exclude patterns from PATTERNFILE, one per line]:_files'\ - --strip-components'[Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped.]:NUMBER'\ - $borg_common_options - ;; - (check) - _arguments \ - '2:archives:__borg_archive'\ - --repository-only'[only perform repository checks]'\ - --archives-only'[only perform archives checks]'\ - --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]:SECONDS'\ - {-P,--prefix}'[only consider archive names starting with this prefix.]:PREFIX'\ - {-a,--glob-archives}'[only consider archive names matching the glob]:GLOB'\ - --sort-by'[Comma-separated list of sorting keys]:keys:(timestamp name id)'\ - --first'[consider first N archives after other filters were applied]:N'\ - --last'[consider last N archives after other filters were applied]:N'\ - $borg_common_options - ;; - (rename) - _arguments \ - '2:archives:__borg_archive'\ - '3:name:NAME'\ - $borg_common_options - ;; - (list) - _arguments \ - '2:archives:__borg_archive'\ - '*:path:_files'\ - --short'[only print file/directory names, nothing else]'\ - --format'[specify format for file listing]:FORMAT'\ - --json'[Only valid for listing repository contents. Format output as JSON.]'\ - --json-lines'[Only valid for listing archive contents. Format output as JSON Lines. ]'\ - {-P,--prefix}'[only consider archive names starting with this prefix.]:PREFIX'\ - {-a,--glob-archives}'[only consider archive names matching the glob]:GLOB'\ - --sort-by'[Comma-separated list of sorting keys]:keys:(timestamp name id)'\ - --first'[consider first N archives after other filters were applied]:N'\ - --last'[consider last N archives after other filters were applied]:N'\ - {-e,--exclude}'[exclude paths matching PATTERN]:PATTERN'\ - --exclude-from'[read exclude patterns from EXCLUDEFILE, one per line]:_files'\ - --pattern'[experimental: include/exclude paths matching PATTERN]:PATTERN'\ - --patterns-from'[experimental: read include/exclude patterns from PATTERNFILE, one per line]:_files'\ - $borg_common_options - ;; - (diff) - _arguments \ - '2:archives:__borg_archive'\ - '3:archives:__borg_archive2'\ - '*:path:_files'\ - --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.]'\ - {-e,--exclude}'[exclude paths matching PATTERN]:PATTERN'\ - --exclude-from'[read exclude patterns from EXCLUDEFILE, one per line]:_files'\ - --pattern'[experimental: include/exclude paths matching PATTERN]:PATTERN'\ - --patterns-from'[experimental: read include/exclude patterns from PATTERNFILE, one per line]:_files'\ - $borg_common_options + case $state in + (command) + _borg_commands && ret=0 + ;; + (option-or-argument) + curcontext="${curcontext%:*:*}:borg-$words[1]:" - ;; - (delete) - _arguments \ - '2:archives:__borg_archive'\ - '*:archives:archives'\ - --dry-run'[do not change the repository]'\ - {-s,--stats}'[print statistics for the deleted archive]'\ - --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.]'\ - --save-space'[work slower, but using less space]'\ - {-P,--prefix}'[only consider archive names starting with this prefix.]:PREFIX'\ - {-a,--glob-archives}'[only consider archive names matching the glob]:GLOB'\ - --sort-by'[Comma-separated list of sorting keys]:keys:(timestamp name id)'\ - --first'[consider first N archives after other filters were applied]:N'\ - --last'[consider last N archives after other filters were applied]:N'\ - $borg_common_options - ;; - (prune) - _arguments \ - '2:archives:__borg_archive'\ - {-n,--dry-run}'[do not change repository]'\ - --force'[force pruning of corrupted archives]'\ - {-s,--stats}'[print statistics for the deleted archive]'\ - --list'[output verbose list of archives it keeps/prunes]'\ - --keep-within'[keep all archives within this time interval]:INTERVAL'\ - {--keep-last,--keep-secondly}'[number of secondly archives to keep]:N'\ - --keep-minutely'[number of minutely archives to keep]:N'\ - {-H,--keep-hourly}'[number of hourly archives to keep]:N'\ - {-d,--keep-daily}'[number of daily archives to keep]:N'\ - {-w,--keep-weekly}'[number of weekly archives to keep]:N'\ - {-m,--keep-monthly}'[number of monthly archives to keep]:N'\ - {-y,--keep-yearly}'[number of yearly archives to keep]:N'\ - --save-space'[work slower, but using less space]'\ - {-P,--prefix}'[only consider archive names starting with this prefix.]:PREFIX'\ - {-a,--glob-archives}'[only consider archive names matching the glob]:GLOB'\ - $borg_common_options - ;; - (compact) - _arguments \ - '2:archives:__borg_archive'\ - --cleanup-commits'[cleanup commit-only 17-byte segment files]'\ - $borg_common_options - ;; - (info) - _arguments \ - '2:archives:__borg_archive'\ - --json'[format output as JSON]'\ - {-P,--prefix}'[only consider archive names starting with this prefix.]:PREFIX'\ - {-a,--glob-archives}'[only consider archive names matching the glob]:GLOB'\ - --sort-by'[Comma-separated list of sorting keys]:keys:(timestamp name id)'\ - --first'[consider first N archives after other filters were applied]:N'\ - --last'[consider last N archives after other filters were applied]:N'\ - $borg_common_options - ;; - (mount) - _arguments \ - '2:archives:__borg_archive'\ - '3:mountpoint:_files'\ - {-f,--foreground}'[stay in foreground, do not daemonize]'\ - -o'[Extra mount options]:options:(ac_attr_timeout= allow_damaged_files allow_other allow_root attr_timeout= auto auto_cache auto_unmount default_permissions entry_timeout= gid= group_id= kernel_cache max_read= negative_timeout= noauto noforget remember= remount rootmode= uid= umask= user user_id= versions)'\ - {-P,--prefix}'[only consider archive names starting with this prefix.]:PREFIX'\ - {-a,--glob-archives}'[only consider archive names matching the glob]:GLOB'\ - --sort-by'[Comma-separated list of sorting keys]:keys:(timestamp name id)'\ - --first'[consider first N archives after other filters were applied]:N'\ - --last'[consider last N archives after other filters were applied]:N'\ - {-e,--exclude}'[exclude paths matching PATTERN]:PATTERN'\ - --exclude-from'[read exclude patterns from EXCLUDEFILE, one per line]:_files'\ - --pattern'[experimental: include/exclude paths matching PATTERN]:PATTERN'\ - --patterns-from'[experimental: read include/exclude patterns from PATTERNFILE, one per line]:_files'\ - --strip-components'[Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped.]:NUMBER'\ - $borg_common_options - ;; - (umount) - _arguments \ - '2:mountpoint:_files'\ - $borg_common_options - ;; - (key) - case $keyCommand in - (change-passphrase) - _arguments \ - '2:subCommand:(change-passphrase import export)'\ - '3:archives:__borg_archive'\ - $borg_common_options - ;; - (export) - _arguments \ - '2:subCommand:(change-passphrase import export)'\ - '3:archives:__borg_archive'\ - '4:path:_files'\ - --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]'\ - $borg_common_options - ;; - (import) - _arguments \ - '2:subCommand:(change-passphrase import export)'\ - '3:archives:__borg_archive'\ - '4:path:_files'\ - --paper'[interactively import from a backup done with --paper]'\ - $borg_common_options - ;; - *) - _arguments \ - '2:subCommand:(change-passphrase import export)'\ - $borg_common_options - ;; - esac - ;; - (upgrade) - _arguments \ - '2:archives:__borg_archive'\ - {-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).]'\ - $borg_common_options - ;; - (recreate) - _arguments \ - '2:archives:__borg_archive'\ - '3:path:_files'\ - --list'[output verbose list of items (files, dirs, ...)]'\ - --filter'[only display items with the given status characters]:STATUSCHARS'\ - {-n,--dry-run}'[do not create a backup archive]'\ - {-s,--stats}'[print statistics at end]'\ - {-e,--exclude}'[exclude paths matching PATTERN]:PATTERN'\ - --exclude-from'[read exclude patterns from EXCLUDEFILE, one per line]:_files'\ - --pattern'[experimental: include/exclude paths matching PATTERN]:PATTERN'\ - --patterns-from'[experimental: read include/exclude patterns from PATTERNFILE, one per line]:_files'\ - --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'\ - --keep-exclude-tags'[if tag objects are specified with --exclude-if-present, don’t omit the tag objects themselves]'\ - --target'[create a new archive with the name ARCHIVE]:ARCHIVE'\ - {-c,--checkpoint-interval}'[write checkpoint every SECONDS seconds]:SECONDS'\ - --comment'[add a comment text to the archive]:COMMENT'\ - --timestamp'[manually specify the archive creation date/time]:TIMESTAMP'\ - {-C,--compression}'[select compression algorithm]:COMPRESSION'\ - --recompress'[recompress data chunks according to --compression if if-different]:params:(if-different always)'\ - --chunker-params'[pecify the chunker parameters]:PARAMS'\ - $borg_common_options - ;; - (export-tar) - _arguments \ - '2:archives:__borg_archive'\ - '3:tar:_files'\ - '4:path:_files'\ - --tar-filter'[filter program to pipe data through]'\ - --list'[output verbose list of items (files, dirs, ...)]'\ - {-e,--exclude}'[exclude paths matching PATTERN]:PATTERN'\ - --exclude-from'[read exclude patterns from EXCLUDEFILE, one per line]:_files'\ - --pattern'[experimental: include/exclude paths matching PATTERN]:PATTERN'\ - --patterns-from'[experimental: read include/exclude patterns from PATTERNFILE, one per line]:_files'\ - --strip-components'[Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped.]:NUMBER'\ - $borg_common_options - ;; - (serve) - _arguments \ - --restrict-to-path'[restrict repository access to PATH]:_files'\ - --restrict-to-repository'[restrict repository access]:_files'\ - --append-only'[only allow appending to repository segment files]'\ - --storage-quota'[Override storage quota of the repository]:QUOTA'\ - $borg_common_options - ;; - (config) - _arguments \ - '2:archives:__borg_archive'\ - '3:name:NAME'\ - '4:value:VALUE'\ - {-c,--cache}'[get and set values from the repo cache]'\ - {-d,--delete}'[delete the key from the config]'\ - --list'[list the configuration of the repo]'\ - $borg_common_options - ;; - (with-lock) - _arguments \ - '(-)2:archives:__borg_archive'\ - $borg_common_options - #'3:command:_command_names -e'\ - #'4:arguments:_normal' - #TODO Debug this, getting "_tags:comptags:36: nesting level too deep" error - ;; - (break-lock) - _arguments \ - '2:archives:__borg_archive'\ - $borg_common_options - ;; - (benchmark) - _arguments \ - '2:type:(crud)'\ - '3:repo:_files'\ - '4:path:_files'\ - $borg_common_options - ;; - (help) - _arguments \ - '2:type:(patterns placeholders compression )'\ - $borg_common_options - ;; - *) - commands=( - 'init:initialize empty repository' - 'create:create backup' - 'extract:extract archive contents' - 'check:verify repository' - 'rename:rename archive' - 'list:list archive or repository contents' - 'diff:find differences in archive contents' - 'delete:delete archive' - 'prune:prune archives' - 'compact:free repository space' - 'info:show repository or archive information' - 'mount:mount repository' - 'umount:umount repository' - 'key:manage repository key' - 'upgrade:upgrade repository format' - 'recreate:recreate contents of existing archives' - 'export-tar:create tarball from archive' - 'serve:start repository server process' - 'config:get/set options in repo/cache config' - 'with-lock:run user command with lock held' - 'break-lock:break repository and cache locks' - 'benchmark:benchmark command' - 'help:miscellaneous help' - ) - - _describe 'values' commands - _arguments $borg_common_options - - ;; + 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_archive() { - __borg_list_archives 1 -} -__borg_archive2() { - __borg_list_archives 2 -} - -__borg_list_archives() { - local -a items - local cpath - cpath=`expr match "${words}" "\(.*\)::"` - cpath=${cpath##* } - if (( $1 == 1 )); then - # To achieve "repository::archive" listing: - prefix_repo="${cpath}::" - fi - if (( $1 == 2 )); then - # To achieve only "archive" listing: - prefix_repo= - fi - items=("${(@f)$(borg list --format=$prefix_repo\{archive\}\{NEWLINE\} $cpath 2>/dev/null)}") - if [[ $items[1] == "" ]]; then - _files -/ - else - _wanted archives expl 'archive' compadd $items - fi -} +_borg "$@"