#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]' \ '--noacls[do not read and store ACLs into archive]' \ '--noxattrs[do not read and store xattrs 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 size 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' 'dump-hints:dump repository hints' '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 ;; (dump-hints) _arguments -s -w -S : \ $common_options \ ': :_borg_repository' \ '*:PATH:_files' && 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]' \ '--json-lines[format output as JSON Lines]' \ $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]' \ '--noacls[do not extract/set ACLs]' \ '--noxattrs[do not extract/set xattrs]' \ '--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 explicitly to 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"' '--remote-buffer=[set upload buffer size in MiB. (default: 0=no buffer)]: : _borg_guard_unsigned_number "UPLOAD_BUFFER"' '--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 ;; (SELFTEST) _description values expl 'value' compadd "$expl[@]" disabled && 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 command_line 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 workaround 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:("zstandard")' '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 "$@"