_borg 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584
  1. #compdef borg borgfs -P -value-,BORG_*,-default-
  2. # Zsh completion for Borg Backup 1.1.14 (2020-09-27).
  3. #
  4. # Recommended _borg specific settings:
  5. #
  6. # zstyle -e ':completion:*:*:borg-*:argument-rest:*' tag-order \
  7. # '[[ $words[CURRENT] == -* ]] && reply=( "! archives archive-files" "-" )'
  8. # zstyle ':completion:*:*:(borg|-value-,BORG_)*' sort false
  9. # zstyle ':completion:*:*:borg-config:argument-2:keys' list-grouped false
  10. # zstyle ':completion:*:*:borg-*:*' gain-privileges true
  11. # zstyle ':completion:*' fake-parameters 'BORG_REPO:scalar'
  12. #
  13. # Custom styles:
  14. #
  15. # archive-description-format
  16. # Default: `{archive:<36} {time} [{id}]`.
  17. # archive-sort
  18. # In which order archive names should be listed.
  19. # Possible values are: `inverse`, `timestamp`, `name`, `id`.
  20. # file-description-format
  21. # Default: `{mode} {user:6} {group:6} {size:8d} {mtime} {path}{extra}`.
  22. # path-style-selector
  23. # Style selector used to select a path (when Borg would use either `pp` or `pf`).
  24. # Default: `fm`.
  25. # repository-suffix
  26. # This boolean style controls whether to add the `::` auto-removable suffix to a repository.
  27. # Default: `true`.
  28. (( $+functions[_borg_commands] )) ||
  29. _borg_commands() {
  30. local -a commands_=(
  31. 'benchmark:benchmark command'
  32. 'break-lock:break repository and cache locks'
  33. 'change-passphrase:change repository passphrase'
  34. 'check:verify repository'
  35. 'config:get and set configuration values'
  36. 'create:create backup'
  37. 'debug:debugging command (not intended for normal use)'
  38. 'delete:delete archive'
  39. 'diff:find differences in archive contents'
  40. 'export-tar:create tarball from archive'
  41. 'extract:extract archive contents'
  42. 'help:extra help'
  43. 'info:show repository or archive information'
  44. 'init:initialize empty repository'
  45. 'key:manage repository key'
  46. 'list:list archive or repository contents'
  47. 'mount:mount repository'
  48. 'prune:prune archives'
  49. 'recreate:re-create archives'
  50. 'rename:rename archive'
  51. 'serve:start repository server process'
  52. 'umount:umount repository'
  53. 'upgrade:upgrade repository format'
  54. 'with-lock:run user command with lock held'
  55. )
  56. _describe -t commands 'borg commands' commands_
  57. }
  58. (( $+functions[_borg-benchmark] )) ||
  59. _borg-benchmark() {
  60. local -a common_options
  61. __borg_setup_common_options
  62. _arguments -s -w -S : \
  63. $common_options \
  64. ':type:(crud)' \
  65. ': :_borg_repository' \
  66. ':PATH:_files'
  67. }
  68. (( $+functions[_borg-break-lock] )) ||
  69. _borg-break-lock() {
  70. local -a common_options
  71. __borg_setup_common_options
  72. _arguments -s -w -S : \
  73. $common_options \
  74. ':: :_borg_repository'
  75. }
  76. (( $+functions[_borg-change-passphrase] )) ||
  77. _borg-change-passphrase() {
  78. local -a common_options
  79. __borg_setup_common_options
  80. _arguments -s -w -S : \
  81. $common_options \
  82. ':: :_borg_repository'
  83. }
  84. (( $+functions[_borg-check] )) ||
  85. _borg-check() {
  86. local -a common_options common_archive_filters_options
  87. __borg_setup_common_options
  88. __borg_setup_common_archive_filters_options
  89. _arguments -s -w -S : \
  90. '--repository-only[only perform repository checks]' \
  91. '--archives-only[only perform archives checks]' \
  92. '(--repository-only)--verify-data[perform cryptographic archive data integrity verification]' \
  93. '--repair[attempt to repair any inconsistencies found]' \
  94. '--save-space[work slower, but using less space]' \
  95. $common_archive_filters_options \
  96. $common_options \
  97. '::REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive'
  98. }
  99. (( $+functions[_borg-config] )) ||
  100. _borg-config() {
  101. local -a common_options
  102. __borg_setup_common_options
  103. _arguments -s -w -S : \
  104. '(-c --cache)'{-c,--cache}'[get and set values from the repo cache]' \
  105. '(-d --delete)'{-d,--delete}'[delete the key from the config]' \
  106. '(-l --list)'{-l,--list}'[list the configuration of the repo]' \
  107. $common_options \
  108. ': :_borg_repository' \
  109. ': : _borg_config $line[1]' \
  110. '::VALUE'
  111. }
  112. (( $+functions[_borg-create] )) ||
  113. _borg-create() {
  114. local -a common_options common_create_options
  115. __borg_setup_common_options
  116. __borg_setup_common_create_options
  117. _arguments -s -w -S : \
  118. '*'{-e,--exclude}'=[exclude paths matching PATTERN]: : _borg_style_selector_or_archive_files -f -e "$line[1]" fm "${(@)line[2,-1]}"' \
  119. '*--pattern=[experimental: include/exclude paths matching PATTERN]: : _borg_style_selector_or_archive_files -p -f -e "$line[1]" sh "${(@)line[2,-1]}"' \
  120. $common_create_options \
  121. '(-s --stats)--json[Output stats as JSON. Implies --stats.]' \
  122. '--no-cache-sync[experimental: do not synchronize the cache. Implies not using the files cache.]' \
  123. '--no-files-cache[do not load/update the file metadata cache used to detect unchanged files]' \
  124. '--stdin-name=[use NAME in archive for stdin data (default: "stdin")]:NAME' \
  125. '--exclude-nodump[exclude files flagged NODUMP]' \
  126. '(-x --one-file-system)'{-x,--one-file-system}'[stay in the same file system]' \
  127. '--numeric-owner[only store numeric user and group identifiers]' \
  128. '--noatime[do not store atime into archive]' \
  129. '--noctime[do not store ctime into archive]' \
  130. '--nobirthtime[do not store birthtime (creation date) into archive]' \
  131. '--nobsdflags[do not read and store bsdflags (e.g. NODUMP, IMMUTABLE) into archive]' \
  132. '--ignore-inode[ignore inode data in the file metadata cache used to detect unchanged files]' \
  133. '--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)' \
  134. '--read-special[open and read block and char device files as well as FIFOs as if they were regular files]' \
  135. $common_options \
  136. ':ARCHIVE: _borg_repository_or_archive -a -p' \
  137. '*:PATH:_files'
  138. }
  139. (( $+functions[_borg-debug] )) ||
  140. _borg-debug() {
  141. local -a state line common_options
  142. local curcontext="$curcontext" state_descr
  143. declare -A opt_args
  144. local -i ret=1
  145. __borg_setup_common_options
  146. _arguments -s -w -C : \
  147. $common_options \
  148. ': :->command' \
  149. '*:: :->option-or-argument' && ret=0
  150. case $state in
  151. (command)
  152. local -a debug_commands=(
  153. 'info:show system infos for debugging / bug reports'
  154. 'dump-archive-items:dump archive items (metadata)'
  155. 'dump-archive:dump decoded archive metadata'
  156. 'dump-manifest:dump decoded repository metadata'
  157. 'dump-repo-objs:dump repo objects'
  158. 'search-repo-objs:search repo objects'
  159. 'get-obj:get object from repository'
  160. 'put-obj:put object to repository'
  161. 'delete-obj:delete object from repository'
  162. 'refcount-obj:show refcount for object from repository'
  163. 'convert-profile:convert Borg profile to Python profile'
  164. )
  165. _describe -t commands 'command' debug_commands && ret=0
  166. ;;
  167. (option-or-argument)
  168. curcontext="${curcontext%:*}-$line[1]:"
  169. case $line[1] in
  170. (info)
  171. _arguments -s -w -S : \
  172. $common_options && ret=0
  173. ;;
  174. (dump-archive-items)
  175. _arguments -s -w -S : \
  176. $common_options \
  177. ':ARCHIVE: _borg_repository_or_archive -a' && ret=0
  178. ;;
  179. (dump-archive)
  180. _arguments -s -w -S : \
  181. $common_options \
  182. ':ARCHIVE: _borg_repository_or_archive -a' \
  183. ':PATH:_files' && ret=0
  184. ;;
  185. (dump-manifest)
  186. _arguments -s -w -S : \
  187. $common_options \
  188. ': :_borg_repository' \
  189. ':PATH:_files' && ret=0
  190. ;;
  191. (dump-repo-objs)
  192. _arguments -s -w -S : \
  193. $common_options \
  194. ': :_borg_repository' && ret=0
  195. ;;
  196. (search-repo-objs)
  197. _arguments -s -w -S : \
  198. $common_options \
  199. ': :_borg_repository' \
  200. ':WANTED (hex or string):' && ret=0
  201. ;;
  202. (get-obj)
  203. _arguments -s -w -S : \
  204. $common_options \
  205. ': :_borg_repository' \
  206. ':ID (hex object):' \
  207. ':PATH:_files' && ret=0
  208. ;;
  209. (put-obj)
  210. _arguments -s -w -S : \
  211. $common_options \
  212. ': :_borg_repository' \
  213. '*:PATH:_files' && ret=0
  214. ;;
  215. (delete-obj)
  216. _arguments -s -w -S : \
  217. $common_options \
  218. ': :_borg_repository' \
  219. '*:ID (hex object):' && ret=0
  220. ;;
  221. (refcount-obj)
  222. _arguments -s -w -S : \
  223. $common_options \
  224. ': :_borg_repository' \
  225. '*:ID (hex object):' && ret=0
  226. ;;
  227. (convert-profile)
  228. _arguments -s -w -S : \
  229. $common_options \
  230. ':INPUT:_files' \
  231. ':OUTPUT:_files' && ret=0
  232. ;;
  233. (*)
  234. if ! _call_function ret _borg_debug_$line[1]; then
  235. _default && ret=0
  236. fi
  237. ;;
  238. esac
  239. ;;
  240. esac
  241. return ret
  242. }
  243. (( $+functions[_borg-delete] )) ||
  244. _borg-delete() {
  245. local -a common_options common_archive_filters_options common_dry_run_stats_options
  246. __borg_setup_common_options
  247. __borg_setup_common_archive_filters_options
  248. __borg_setup_common_dry_run_stats_options
  249. _arguments -s -w -S : \
  250. $common_dry_run_stats_options \
  251. '--cache-only[delete only the local cache for the given repository]' \
  252. '*--force[force deletion of corrupted archives, use --force --force in case --force does not work.]' \
  253. '--save-space[work slower, but using less space]' \
  254. $common_archive_filters_options \
  255. $common_options \
  256. ':REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' \
  257. '*:ARCHIVE: _borg_archive "${line[1]%%::*}"'
  258. }
  259. (( $+functions[_borg-diff] )) ||
  260. _borg-diff() {
  261. local -a common_options common_exclude_options
  262. __borg_setup_common_options
  263. __borg_setup_common_exclude_options
  264. _arguments -s -w -S : \
  265. '--numeric-owner[only obey numeric user and group identifiers]' \
  266. '--same-chunker-params[override check of chunker parameters]' \
  267. '--sort[sort the output lines by file path]' \
  268. $common_exclude_options \
  269. $common_options \
  270. ':ARCHIVE1: _borg_repository_or_archive -a' \
  271. ':ARCHIVE2: _borg_archive "${line[1]%%::*}"' \
  272. '*: : _borg_style_selector_or_archive_files -e "$line[1]" pp'
  273. }
  274. (( $+functions[_borg-export-tar] )) ||
  275. _borg-export-tar() {
  276. local -a common_options common_exclude_extract_options
  277. __borg_setup_common_options
  278. __borg_setup_common_exclude_extract_options
  279. _arguments -s -w -S : \
  280. '--tar-filter[filter program to pipe data through]: :_cmdstring' \
  281. '--list[output verbose list of items (files, dirs, ...)]' \
  282. $common_exclude_extract_options \
  283. $common_options \
  284. ':ARCHIVE: _borg_repository_or_archive -a' \
  285. ':FILE:_files' \
  286. '*: : _borg_style_selector_or_archive_files -e "$line[1]" pp'
  287. }
  288. (( $+functions[_borg-extract] )) ||
  289. _borg-extract() {
  290. local -a common_options common_exclude_extract_options
  291. __borg_setup_common_options
  292. __borg_setup_common_exclude_extract_options
  293. _arguments -s -w -S : \
  294. '--list[output verbose list of items (files, dirs, ...)]' \
  295. '(-n --dry-run)'{-n,--dry-run}'[do not actually change any files]' \
  296. '--numeric-owner[only obey numeric user and group identifiers]' \
  297. '--nobsdflags[do not extract/set bsdflags (e.g. NODUMP, IMMUTABLE)]' \
  298. '--stdout[write all extracted data to stdout]' \
  299. '--sparse[create holes in output sparse file from all-zero chunks]' \
  300. $common_exclude_extract_options \
  301. $common_options \
  302. ':ARCHIVE: _borg_repository_or_archive -a' \
  303. '*: : _borg_style_selector_or_archive_files -e "$line[1]" pp'
  304. }
  305. (( $+functions[_borg-help] )) ||
  306. _borg-help() {
  307. local -a common_options
  308. __borg_setup_common_options
  309. _arguments -s -w -S : \
  310. '--epilog-only' \
  311. '--usage-only' \
  312. $common_options \
  313. ':: : _alternative "topics:TOPIC:(patterns placeholders compression)" ": :_borg_commands"'
  314. }
  315. (( $+functions[_borg-info] )) ||
  316. _borg-info() {
  317. local -a common_options common_archive_filters_options
  318. __borg_setup_common_options
  319. __borg_setup_common_archive_filters_options
  320. _arguments -s -w -S : \
  321. '--json[format output as JSON]' \
  322. $common_archive_filters_options \
  323. $common_options \
  324. '::REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive'
  325. }
  326. (( $+functions[_borg-init] )) ||
  327. _borg-init() {
  328. local -i ret=1
  329. local -a common_options common_init_options
  330. __borg_setup_common_options
  331. __borg_setup_common_init_options
  332. # special handling for the required optional argument
  333. if (( ! ${words[(I)(-e|--encryption)(|=*)]} )); then
  334. local desc='select encryption key mode'
  335. local -a long=( "--encryption:$desc" ) short=( "-e:$desc" ) remove_chars=( -r '= \t\n\-' )
  336. _describe -t required-options 'required option' long -S '=' $remove_chars -- short $remove_chars && ret=0
  337. fi
  338. _arguments -s -w -S : \
  339. '(-e --encryption)'{-e,--encryption}'=[select encryption key mode (required)]:MODE:(none keyfile keyfile-blake2 repokey repokey-blake2 authenticated authenticated-blake2)' \
  340. $common_init_options \
  341. '--make-parent-dirs[create parent directories]' \
  342. '::REPOSITORY:_directories' && ret=0
  343. return ret
  344. }
  345. (( $+functions[_borg-key] )) ||
  346. _borg-key() {
  347. local -a state line common_options
  348. local curcontext="$curcontext" state_descr
  349. declare -A opt_args
  350. local -i ret=1
  351. __borg_setup_common_options
  352. _arguments -s -w -C : \
  353. $common_options \
  354. ': :->command' \
  355. '*:: :->option-or-argument' && ret=0
  356. case $state in
  357. (command)
  358. local -a key_commands=(
  359. 'change-passphrase:Change repository key file passphrase'
  360. 'export:Export the repository key for backup'
  361. 'import:Import the repository key from backup'
  362. 'migrate-to-repokey:Migrate passphrase -> repokey'
  363. )
  364. _describe -t commands 'command' key_commands && ret=0
  365. ;;
  366. (option-or-argument)
  367. curcontext="${curcontext%:*}-$line[1]:"
  368. case $line[1] in
  369. (change-passphrase)
  370. _arguments -s -w -S : \
  371. $common_options \
  372. ': :_borg_repository' && ret=0
  373. ;;
  374. (export)
  375. _arguments -s -w -S : \
  376. '--paper[create an export suitable for printing and later type-in]' \
  377. '--qr-html[create an html file suitable for printing and later type-in or qr scan]' \
  378. $common_options \
  379. ': :_borg_repository' \
  380. '::PATH:_files' && ret=0
  381. ;;
  382. (import)
  383. _arguments -s -w -S : \
  384. '--paper[interactively import from a backup done with --paper]' \
  385. $common_options \
  386. ': :_borg_repository' \
  387. '::PATH:_files' && ret=0
  388. ;;
  389. (migrate-to-repokey)
  390. _arguments -s -w -S : \
  391. $common_options \
  392. ':: :_borg_repository' && ret=0
  393. ;;
  394. (*)
  395. if ! _call_function ret _borg_key_$line[1]; then
  396. _default && ret=0
  397. fi
  398. ;;
  399. esac
  400. ;;
  401. esac
  402. return ret
  403. }
  404. (( $+functions[_borg-list] )) ||
  405. _borg-list() {
  406. local -a common_options common_exclude_options common_archive_filters_options
  407. __borg_setup_common_options
  408. __borg_setup_common_exclude_options
  409. __borg_setup_common_archive_filters_options
  410. _arguments -s -w -S : \
  411. '--short[only print file/directory names, nothing else]' \
  412. {--format,--list-format}'=[specify format for file listing]:FORMAT: _borg_format_keys $line[1]' \
  413. '--json[Only valid for listing repository contents. Format output as JSON.]' \
  414. '--json-lines[Only valid for listing archive contents. Format output as JSON Lines.]' \
  415. $common_archive_filters_options \
  416. $common_exclude_options \
  417. $common_options \
  418. ':REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' \
  419. '*: : _borg_style_selector_or_archive_files -e "$line[1]" pp'
  420. }
  421. (( $+functions[_borg-mount] )) ||
  422. _borg-mount() {
  423. local -a common_options common_exclude_extract_options common_archive_filters_options
  424. __borg_setup_common_options
  425. __borg_setup_common_exclude_extract_options
  426. __borg_setup_common_archive_filters_options
  427. _arguments -s -w -S : \
  428. $* \
  429. '(-f --foreground)'{-f,--foreground}'[stay in foreground, do not daemonize]' \
  430. '-o[mount options]: :_fuse_values "mount options"
  431. "versions[merged, versioned view of the files in the archives]"
  432. "allow_damaged_files[read damaged files]"
  433. "ignore_permissions[not enforce \"default_permissions\"]"' \
  434. $common_archive_filters_options \
  435. $common_exclude_extract_options \
  436. $common_options \
  437. ':REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' \
  438. ':MOUNTPOINT:_directories' \
  439. '*: : _borg_style_selector_or_archive_files "$line[1]" pp'
  440. }
  441. (( $+functions[_borg-prune] )) ||
  442. _borg-prune() {
  443. local -a common_options common_prefix_and_glob_archives_filter_options common_dry_run_stats_options
  444. __borg_setup_common_options
  445. __borg_setup_common_prefix_and_glob_archives_filter_options
  446. __borg_setup_common_dry_run_stats_options
  447. _arguments -s -w -S : \
  448. $common_dry_run_stats_options \
  449. '--force[force pruning of corrupted archives]' \
  450. '--list[output verbose list of archives it keeps/prunes]' \
  451. '--keep-within[keep all archives within this time interval]: : _borg_guard_unsigned_number "INTERVAL"' \
  452. '(--keep-last --keep-secondly)'{--keep-last,--keep-secondly}'[number of secondly archives to keep]: : _borg_guard_unsigned_number "N"' \
  453. '--keep-minutely[number of minutely archives to keep]: : _borg_guard_unsigned_number "N"' \
  454. '(-H --keep-hourly)'{-H,--keep-hourly}'[number of hourly archives to keep]: : _borg_guard_unsigned_number "N"' \
  455. '(-d --keep-daily)'{-d,--keep-daily}'[number of daily archives to keep]: : _borg_guard_unsigned_number "N"' \
  456. '(-w --keep-weekly)'{-w,--keep-weekly}'[number of weekly archives to keep]: : _borg_guard_unsigned_number "N"' \
  457. '(-m --keep-monthly)'{-m,--keep-monthly}'[number of monthly archives to keep]: : _borg_guard_unsigned_number "N"' \
  458. '(-y --keep-yearly)'{-y,--keep-yearly}'[number of yearly archives to keep]: : _borg_guard_unsigned_number "N"' \
  459. '--save-space[work slower, but using less space]' \
  460. $common_prefix_and_glob_archives_filter_options \
  461. $common_options \
  462. ':: :_borg_repository'
  463. }
  464. (( $+functions[_borg-recreate] )) ||
  465. _borg-recreate() {
  466. local -a common_options common_create_options
  467. __borg_setup_common_options
  468. __borg_setup_common_create_options
  469. _arguments -s -w -S : \
  470. $common_create_options \
  471. '--target[create a new archive with the name ARCHIVE]:ARCHIVE: _borg_placeholder_or_archive "${line[1]%%\:\:*}"' \
  472. '--recompress[recompress data chunks according to --compression]:params:((if-different always never\:\(default\)))' \
  473. $common_options \
  474. ':REPOSITORY_OR_ARCHIVE: _borg_repository_or_archive' \
  475. '*: : _borg_style_selector_or_archive_files -e "$line[1]" pp'
  476. }
  477. (( $+functions[_borg-rename] )) ||
  478. _borg-rename() {
  479. local -a common_options
  480. __borg_setup_common_options
  481. _arguments -s -w -S : \
  482. $common_options \
  483. ':ARCHIVE: _borg_repository_or_archive -a' \
  484. ':NEWNAME'
  485. }
  486. (( $+functions[_borg-serve] )) ||
  487. _borg-serve() {
  488. local -a common_options common_init_options
  489. __borg_setup_common_options
  490. __borg_setup_common_init_options
  491. _arguments -s -w -S : \
  492. $common_init_options \
  493. '*--restrict-to-path=[restrict repository access to PATH]:PATH:_files' \
  494. '*--restrict-to-repository=[restrict repository access]: :_borg_repository'
  495. }
  496. (( $+functions[_borg-umount] )) ||
  497. _borg-umount() {
  498. local -a common_options
  499. __borg_setup_common_options
  500. _arguments -s -w -S : \
  501. $common_options \
  502. ':MOUNTPOINT:_umountable'
  503. }
  504. (( $+functions[_borg-upgrade] )) ||
  505. _borg-upgrade() {
  506. local -a common_options
  507. __borg_setup_common_options
  508. _arguments -s -w -S : \
  509. '(-n --dry-run)'{-n,--dry-run}'[do not change repository]' \
  510. '--inplace[rewrite repository in place, with no chance of going back to older versions of the repository]' \
  511. '--force[force upgrade]' \
  512. '--tam[enable manifest authentication (in key and cache)]' \
  513. '--disable-tam[disable manifest authentication (in key and cache)]' \
  514. $common_options \
  515. ':: :_borg_repository'
  516. }
  517. (( $+functions[_borg-with-lock] )) ||
  518. _borg-with-lock() {
  519. local -a state line common_options
  520. local curcontext="$curcontext" state_descr
  521. declare -A opt_args
  522. local -i ret=1
  523. __borg_setup_common_options
  524. _arguments -s -w -C -S : \
  525. $common_options \
  526. '(-): :_borg_repository' \
  527. '(-):COMMAND: _command_names -e' \
  528. '(-)*:ARGS:->normal' && ret=0
  529. case $state in
  530. (normal)
  531. shift 2 words
  532. (( CURRENT -= 2 ))
  533. _normal && ret=0
  534. ;;
  535. esac
  536. return ret
  537. }
  538. (( $+functions[__borg_setup_common_options] )) ||
  539. __borg_setup_common_options() {
  540. typeset -ga common_options=(
  541. '(- :)'{-h,--help}'[show this help message and exit]'
  542. '--critical[work on log level CRITICAL]'
  543. '--error[work on log level ERROR]'
  544. '--warning[work on log level WARNING (default)]'
  545. '(--info -v --verbose)'{--info,-v,--verbose}'[work on log level INFO]'
  546. '--debug[work on log level DEBUG]'
  547. '--debug-topic=[enable TOPIC debugging (can be specified multiple times)]:TOPIC'
  548. '(-p --progress)'{-p,--progress}'[show progress information]'
  549. '--log-json[Output one JSON object per log line instead of formatted text.]'
  550. '--lock-wait=[wait at most SECONDS for acquiring a repository/cache lock (default: 1)]: : _borg_guard_unsigned_number "SECONDS"'
  551. '--bypass-lock[bypass locking mechanism]'
  552. '(- :)--show-version[show/log the borg version]'
  553. '--show-rc[show/log the return code (rc)]'
  554. '--umask=[set umask to M (local and remote, default: 0077)]:M'
  555. '--remote-path=[set remote path to executable (default: "borg")]: :_cmdstring'
  556. '--remote-ratelimit=[set remote network upload rate limit in kiByte/s (default: 0=unlimited)]: : _borg_guard_unsigned_number "RATE"'
  557. '--consider-part-files[treat part files like normal files (e.g. to list/extract them)]'
  558. '--debug-profile=[write execution profile in Borg format into FILE]:FILE:_files'
  559. '--rsh=[use COMMAND instead of ssh]: :_cmdstring'
  560. )
  561. }
  562. (( $+functions[__borg_setup_common_exclude_options] )) ||
  563. __borg_setup_common_exclude_options() {
  564. typeset -ga common_exclude_options=(
  565. '*'{-e,--exclude}'=[exclude paths matching PATTERN]: : _borg_style_selector_or_archive_files "$line[1]" fm'
  566. '*--exclude-from=[read exclude patterns from EXCLUDEFILE, one per line]:EXCLUDEFILE:_files'
  567. '*--pattern=[experimental: include/exclude paths matching PATTERN]: : _borg_style_selector_or_archive_files -p "$line[1]" sh'
  568. '*--patterns-from=[experimental: read include/exclude patterns from PATTERNFILE, one per line]:PATTERNFILE:_files'
  569. )
  570. }
  571. (( $+functions[__borg_setup_common_exclude_extract_options] )) ||
  572. __borg_setup_common_exclude_extract_options() {
  573. local -a common_exclude_options
  574. __borg_setup_common_exclude_options
  575. typeset -ga common_exclude_extract_options=(
  576. $common_exclude_options
  577. '--strip-components=[Remove the specified number of leading path elements. Paths with fewer elements will be silently skipped.]: : _borg_guard_unsigned_number "NUMBER"'
  578. )
  579. }
  580. (( $+functions[__borg_setup_common_prefix_and_glob_archives_filter_options] )) ||
  581. __borg_setup_common_prefix_and_glob_archives_filter_options() {
  582. typeset -ga common_prefix_and_glob_archives_filter_options=(
  583. '(-P --prefix -a --glob-archives)'{-P,--prefix}'=[only consider archive names starting with this prefix]:PREFIX: _borg_archive -n "${line[1]%%\:\:*}"'
  584. '(-P --prefix)*'{-a,--glob-archives}'=[only consider archive names matching the glob]:GLOB: _borg_archive -n "${line[1]%%\:\:*}"'
  585. )
  586. }
  587. (( $+functions[__borg_setup_common_archive_filters_options] )) ||
  588. __borg_setup_common_archive_filters_options() {
  589. local -a common_prefix_and_glob_archives_filter_options
  590. __borg_setup_common_prefix_and_glob_archives_filter_options
  591. typeset -ga common_archive_filters_options=(
  592. $common_prefix_and_glob_archives_filter_options
  593. '--sort-by=[Comma-separated list of sorting keys, default: timestamp]:KEYS:(timestamp name id)'
  594. '(--last)--first=[consider first N archives after other filters were applied]:N: _borg_archive -n "${line[1]%%\:\:*}"'
  595. '(--first)--last=[consider last N archives after other filters were applied]:N: _borg_archive -n "${line[1]%%\:\:*}"'
  596. )
  597. }
  598. (( $+functions[__borg_setup_common_dry_run_stats_options] )) ||
  599. __borg_setup_common_dry_run_stats_options() {
  600. typeset -ga common_dry_run_stats_options=(
  601. '(-n --dry-run -s --stats)'{-n,--dry-run}'[do not change anything]'
  602. '(-n --dry-run -s --stats)'{-s,--stats}'[print statistics at end]'
  603. # NOTE: actual messages for subcommands differ in details
  604. )
  605. }
  606. (( $+functions[__borg_setup_common_create_options] )) ||
  607. __borg_setup_common_create_options() {
  608. local -a common_dry_run_stats_options common_exclude_options
  609. __borg_setup_common_dry_run_stats_options
  610. __borg_setup_common_exclude_options
  611. typeset -ga common_create_options=(
  612. $common_dry_run_stats_options
  613. '--list[output verbose list of items (files, dirs, ...)]'
  614. '--filter=[only display items with the given status characters]: :_borg_statuschars'
  615. $common_exclude_options
  616. '--exclude-caches[exclude directories that contain a CACHEDIR.TAG file]'
  617. '*--exclude-if-present=[exclude directories that are tagged by containing a filesystem object with the given NAME]:NAME:_files'
  618. '--keep-'{exclude-tags,tag-files}'[if tag objects are specified with --exclude-if-present, don'\''t omit the tag objects themselves]'
  619. '--comment=[add a comment text to the archive]:COMMENT:_borg_placeholders'
  620. '--timestamp=[manually specify the archive creation date/time]:TIMESTAMP:_borg_timestamp'
  621. '(-c --checkpoint-interval)'{-c,--checkpoint-interval}'=[write checkpoint every SECONDS seconds (default: 1800)]: : _borg_guard_unsigned_number "SECONDS"'
  622. '--chunker-params=[specify the chunker parameters]: :_borg_chunker_params'
  623. '(-C --compression)'{-C,--compression}'=[select compression algorithm]: :_borg_compression'
  624. )
  625. }
  626. (( $+functions[__borg_setup_common_init_options] )) ||
  627. __borg_setup_common_init_options() {
  628. local -a common_options
  629. __borg_setup_common_options
  630. typeset -ga common_init_options=(
  631. $common_options
  632. '--append-only[only allow appending to repository segment files]'
  633. '--storage-quota=[override storage quota of the repository]: :_borg_quota_suffixes'
  634. )
  635. }
  636. (( $+functions[_borgfs] )) ||
  637. _borgfs() {
  638. _borg-mount '(- :)'{-V,--version}'[show version number and exit]'
  639. }
  640. (( $+functions[_borg_parameters] )) ||
  641. _borg_parameters() {
  642. local name=$1
  643. shift
  644. local -i ret=1
  645. local -a expl
  646. case $name in
  647. (REPO)
  648. local BORG_REPO
  649. unset BORG_REPO
  650. _borg_repository && ret=0
  651. ;;
  652. ((|NEW_)PASSPHRASE)
  653. _message -e 'passphrase' && ret=0
  654. ;;
  655. (DISPLAY_PASSPHRASE)
  656. _message -e 'answer to the "display the passphrase for verification" question' && ret=0
  657. ;;
  658. (HOST_ID)
  659. _message -e 'unique ID' && ret=0
  660. ;;
  661. (FILES_CACHE_TTL)
  662. _borg_guard_unsigned_number 'time to live (default: 20)' && ret=0
  663. ;;
  664. (MOUNT_DATA_CACHE_ENTRIES)
  665. _borg_guard_unsigned_number 'number of cached data chunks' && ret=0
  666. ;;
  667. (PASSCOMMAND|RSH|REMOTE_PATH)
  668. _cmdstring && ret=0
  669. ;;
  670. (HOSTNAME_IS_UNIQUE|SHOW_SYSINFO|(UNKNOWN_UNENCRYPTED|RELOCATED)_REPO_ACCESS_IS_OK)
  671. _description values expl 'value'
  672. compadd "$expl[@]" yes no && ret=0
  673. ;;
  674. ((CHECK|DELETE)_I_KNOW_WHAT_I_AM_DOING)
  675. _description values expl 'value'
  676. compadd "$expl[@]" YES NO && ret=0
  677. ;;
  678. (WORKAROUNDS)
  679. _wanted workarounds expl 'workaround' _sequence compadd - basesyncfile && ret=0
  680. ;;
  681. (*)
  682. _default && ret=0
  683. ;;
  684. esac
  685. return ret
  686. }
  687. (( $+functions[_borg_repository] )) ||
  688. _borg_repository() {
  689. local -a alts opts qopts
  690. zparseopts -E -a opts S:
  691. qopts=( ${(q)opts} )
  692. [[ -n $BORG_REPO ]] && alts+=( "default-repository: : __borg_default_repository $qopts" )
  693. alts+=( "cached-repositories:cached repositories:_borg_cached_repositories $qopts" )
  694. alts+=( 'directories: :_directories -r ":/ \t\n\-"' )
  695. alts+=( 'remote-repositories: : _borg_remote_repositories' )
  696. _alternative $alts
  697. }
  698. (( $+functions[__borg_default_repository] )) ||
  699. __borg_default_repository() {
  700. local -a opts suf
  701. zparseopts -E -a opts S:
  702. (( $opts[(I)-S] )) && suf=( -S '' )
  703. local -a default_repository=( "\:\::$BORG_REPO" )
  704. _describe -t default-repository 'default repository' default_repository "$suf[@]"
  705. }
  706. (( $+functions[_borg_cached_repositories] )) ||
  707. _borg_cached_repositories() {
  708. local -a cached_repos
  709. local sed_script='/^previous_location = / {
  710. s///
  711. # no port was given
  712. /ssh:\/\/[^/:]+:[0-9]+/! {
  713. # lstrip the `ssh://` prefix and add a colon before the first slash
  714. s!ssh://([^:/]+)/(.*)!\1:/\2!
  715. }
  716. p
  717. }'
  718. local cachedir=${BORG_CACHE_DIR:-${XDG_CACHE_HOME:-${BORG_BASE_DIR:-$HOME}/.cache}/borg}
  719. cached_repos=( ${(f)"$(_call_program -p cached-repositories sed -n -E ${(q)sed_script} \
  720. "${(q)cachedir}/*/config(#qN.om)" 2>/dev/null)"} )
  721. if [[ $compstate[quote] != (\'|\") ]]; then
  722. # hide ~BORG_REPO and other scalars
  723. local BORG_REPO
  724. unset BORG_REPO sed_script cachedir
  725. cached_repos=( "${(@D)cached_repos}" )
  726. fi
  727. compadd -Q "$@" -r ': \t\n\-' -a cached_repos
  728. }
  729. (( $+functions[_borg_remote_repositories] )) ||
  730. _borg_remote_repositories() {
  731. local -a match mbegin mend expl alts
  732. if compset -P '(#b)ssh://[^/]##@[^/]##:([0-9]##)/'; then
  733. _remote_files -/ -- ssh -p $match[1]
  734. return
  735. fi
  736. local -i have_scheme=0
  737. compset -P 'ssh://' && have_scheme=1
  738. if compset -P '*:'; then
  739. (( have_scheme )) && alts+=( 'ports: : _borg_guard_unsigned_number "port"' )
  740. alts+=( 'remote-files:remote file: _remote_files -/ -- ssh' )
  741. _alternative $alts
  742. elif compset -P 1 '(#b)(*)@'; then
  743. local user=$match[1]
  744. _wanted -C user-at hosts expl "host for $user" \
  745. _combination -s '[:@]' accounts users-hosts users="$user" hosts -S ':' -
  746. elif compset -S '@*'; then
  747. _wanted users expl "user" \
  748. _combination -s '[:@]' accounts users-hosts users -q -
  749. else
  750. alts=(
  751. 'users:user:_users -S "@"'
  752. 'hosts:host:_hosts -S ":"'
  753. )
  754. (( ! have_scheme )) && alts+=( 'prefixes:ssh:compadd -S "" ssh://' )
  755. _alternative $alts
  756. fi
  757. }
  758. # _borg_repository_or_archive [-a] [-p]
  759. #
  760. # -a archive is mandatory. The suffix `::` will be added to the repository if possible.
  761. # -p complete placeholders
  762. (( $+functions[_borg_repository_or_archive] )) ||
  763. _borg_repository_or_archive() {
  764. local -A opts
  765. zparseopts -A opts -D -E a p
  766. if compset -P 1 '*::'; then
  767. local qrepo=$IPREFIX[1,-3]
  768. local -i def_repo=0
  769. [[ -z $qrepo && -n $BORG_REPO ]] && qrepo=${(q)BORG_REPO} && def_repo=1
  770. if [[ -n $qrepo ]]; then
  771. if (( ! def_repo )); then
  772. case $compstate[quote] in
  773. (\') qrepo=${(qq)qrepo} ;;
  774. (\") qrepo=${(qqq)${(e)qrepo}} ;;
  775. # NOTE: currently `(e)` don't have any effect, but maybe one day zsh will stop to change the quoting method
  776. # of double quoted parameters
  777. esac
  778. fi
  779. if (( $+opts[-p] )); then
  780. _borg_placeholder_or_archive $qrepo
  781. else
  782. _borg_archive $qrepo
  783. fi
  784. else
  785. _message "not a borg repository: ${(Q)qrepo}"
  786. return 1
  787. fi
  788. else
  789. local -a suf
  790. if ! compset -S '::*'; then
  791. if (( $+opts[-a] )) || zstyle -T ":completion:${curcontext}:repositories" repository-suffix; then
  792. suf=( -S '::' )
  793. fi
  794. local oqrepo="$PREFIX$SUFFIX"
  795. local qrepo=$oqrepo
  796. [[ $compstate[quote] != (\'|\") ]] && qrepo=${(Q)qrepo}
  797. if __borg_is_borg_repo $qrepo; then
  798. qrepo=${oqrepo%%/}
  799. [[ -z $SUFFIX ]] && PREFIX=${PREFIX%%/} || SUFFIX=${SUFFIX%%/}
  800. compadd -S '::' -r ':/ \t\n\-' -Q -- $qrepo
  801. return
  802. fi
  803. fi
  804. _borg_repository "$suf[@]"
  805. fi
  806. }
  807. # _borg_archive [-F] [-n] [qrepo]
  808. #
  809. # -F don't apply archive filter options on the command line
  810. # -n reverse order, disable matchers and don't do menu completion/selection
  811. (( $+functions[_borg_archive] )) ||
  812. _borg_archive() {
  813. local -A opts
  814. zparseopts -A opts -D -E F n
  815. local qrepo=$1
  816. if [[ -z $qrepo ]]; then
  817. if [[ -n $BORG_REPO ]]; then
  818. qrepo=${(q)BORG_REPO}
  819. else
  820. _message 'no repository specified'
  821. return 1
  822. fi
  823. fi
  824. local -i ret=1
  825. _tags archives
  826. while _tags; do
  827. if _requested archives; then
  828. local -a expl disp archive_filters
  829. local -i reversed_order=1
  830. if (( ! $+opts[-F] )); then
  831. local -a archive_filter_options=( -P --prefix -a --glob-archives --first --last --sort-by ) tmp
  832. local k
  833. for k in $archive_filter_options; do
  834. if [[ -n $opt_args[$k] ]]; then
  835. IFS=: read -A tmp <<<$opt_args[$k]
  836. archive_filters+=( $k=${^tmp:#} )
  837. fi
  838. done
  839. fi
  840. if (( $+opts[-n] )); then
  841. __borg_skip_pattern_matching || return 1
  842. disp+=( -U )
  843. compstate[insert]=''
  844. compstate[list]='list force'
  845. reversed_order=0
  846. fi
  847. local -a asort
  848. zstyle -a ":completion:${curcontext}:archives" archive-sort asort
  849. if (( $asort[(I)inverse] )); then
  850. (( reversed_order = ! reversed_order ))
  851. fi
  852. local -a sort_by=( --sort-by=${(M)^asort:#(timestamp|name|id)} )
  853. # NOTE: in case of option repetition, the later one takes precedence
  854. if (( ! $+__borg_archives_need_update )); then
  855. comppostfuncs+=( __borg_unset_archives_need_update )
  856. typeset -gHi __borg_archives_need_update=1
  857. if (( ! $#archive_filters && ! $+opts[-n] )); then
  858. local erepo
  859. [[ -n $1 ]] && __borg_expand_path ${(Q)qrepo} erepo
  860. local -a newest_file=( $erepo/(hints|index|integrity).<1->(#qN.om[1]) )
  861. if [[ -n $newest_file ]]; then
  862. if zmodload -F zsh/stat b:zstat 2>/dev/null; then
  863. local -a stats
  864. zstat -A stats +mtime $newest_file
  865. local -i mtime=$stats[1]
  866. if [[ $__borg_prev_repo == $erepo
  867. && __borg_prev_mtime -ge mtime
  868. && $__borg_prev_order == $reversed_order
  869. && $__borg_prev_sort_by == $sort_by ]]
  870. then
  871. __borg_archives_need_update=0
  872. else
  873. typeset -gH __borg_prev_repo=$erepo
  874. typeset -gHi __borg_prev_mtime=mtime __borg_prev_order=reversed_order
  875. typeset -gHa __borg_prev_sort_by=( $sort_by )
  876. fi
  877. fi
  878. fi
  879. else
  880. unset __borg_prev_{repo,mtime,order,sort_by}
  881. comppostfuncs+=( __borg_unset_archives )
  882. fi
  883. fi
  884. if zstyle -t ":completion:${curcontext}:archives" verbose; then
  885. if (( __borg_archives_need_update || ! $+__borg_archive_names || ! $+__borg_archive_descriptions )); then
  886. __borg_archives_need_update=0
  887. typeset -gHa __borg_archive_names=() __borg_archive_descriptions=()
  888. local fmt descfmt name desc
  889. zstyle -s ":completion:${curcontext}:archives" archive-description-format descfmt ||
  890. descfmt='{archive:<36} {time} [{id}]'
  891. fmt="{barchive}{NUL}$descfmt{NUL}"
  892. _call_program -p archive-descriptions \
  893. ${(q)__borg_command:-borg} list --format=${(q)fmt} ${(q)sort_by} $archive_filters $qrepo 2>/dev/null |
  894. while IFS= read -r -d $'\0' name && IFS= read -r -d $'\0' descr; do
  895. __borg_archive_names[1,0]=( $name )
  896. __borg_archive_descriptions[1,0]=( "$descr" )
  897. done
  898. (( $pipestatus[1] )) && {
  899. _message "couldn't list repository: ${(Q)qrepo}"
  900. unset __borg_prev_{repo,mtime,order,sort_by}
  901. return 1
  902. }
  903. (( ! reversed_order )) &&
  904. __borg_archive_names=( "${(@aO)__borg_archive_names}" ) &&
  905. __borg_archive_descriptions=( "${(@aO)__borg_archive_descriptions}" )
  906. fi
  907. disp+=( -ld __borg_archive_descriptions )
  908. elif (( __borg_archives_need_update || ! $+__borg_archive_names )); then
  909. __borg_archives_need_update=0
  910. typeset -gHa __borg_archive_names=()
  911. local fmt='{barchive}{NUL}'
  912. __borg_archive_names=( ${(@0aO)"$(_call_program -p archives \
  913. ${(q)__borg_command:-borg} list --format=${(q)fmt} ${(q)sort_by} $archive_filters $qrepo 2>/dev/null)"} )
  914. (( $pipestatus[1] )) && {
  915. _message "couldn't list repository: ${(Q)qrepo}"
  916. unset __borg_prev_{repo,mtime,order,sort_by}
  917. return 1
  918. }
  919. (( ! reversed_order )) &&
  920. __borg_archive_names=( "${(@aO)__borg_archive_names}" )
  921. fi
  922. _all_labels archives expl 'ARCHIVE' compadd "$disp[@]" -a __borg_archive_names && ret=0
  923. fi
  924. (( ret )) || return 0
  925. done
  926. return 1
  927. }
  928. (( $+functions[__borg_unset_archives] )) ||
  929. __borg_unset_archives() {
  930. unset __borg_archive_names __borg_archive_descriptions
  931. }
  932. (( $+functions[__borg_unset_archives_need_update] )) ||
  933. __borg_unset_archives_need_update() {
  934. unset __borg_archives_need_update
  935. }
  936. (( $+functions[__borg_is_borg_repo] )) ||
  937. __borg_is_borg_repo() {
  938. local repo=$1
  939. __borg_expand_path $repo repo
  940. if [[ -d $repo && -d $repo/data ]]; then
  941. local -a files=( $repo/(hints|index|integrity).<1->(#qN.) )
  942. (( $#files >= 3 )) && return 0
  943. fi
  944. return 1
  945. }
  946. (( $+functions[__borg_expand_path] )) ||
  947. __borg_expand_path() {
  948. local _path=$1
  949. local -a match mbegin mend
  950. if [[ $_path == (#b)(\~[^/]#)(|/*) ]]; then
  951. local etilde
  952. etilde=$~match[1] 2>/dev/null
  953. _path="$etilde$match[2]"
  954. fi
  955. _path=${(e)_path//\\\\/\\\\\\\\}
  956. eval typeset -g ${2:-REPLY}=\$_path
  957. }
  958. (( $+functions[_borg_placeholder_or_archive] )) ||
  959. _borg_placeholder_or_archive() {
  960. local qrepo=$1
  961. shift
  962. _alternative \
  963. 'placeholders: :_borg_placeholders' \
  964. "archives: : _borg_archive ${(q)qrepo}"
  965. }
  966. (( $+functions[_borg_placeholders] )) ||
  967. _borg_placeholders() {
  968. local -a placeholders=(
  969. 'hostname:The (short) hostname of the machine.'
  970. 'fqdn:The full name of the machine.'
  971. 'reverse-fqdn:The full name of the machine in reverse domain name notation.'
  972. '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}'
  973. '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}'
  974. 'user:The user name (or UID, if no name is available) of the user running borg.'
  975. 'pid:The current process ID.'
  976. 'borgversion:The version of borg, e.g.: 1.0.8rc1'
  977. 'borgmajor:The version of borg, only the major version, e.g.: 1'
  978. 'borgminor:The version of borg, only major and minor version, e.g.: 1.0'
  979. 'borgpatch:The version of borg, only major, minor and patch version, e.g.: 1.0.8'
  980. )
  981. __borg_complete_keys _describe -t placeholders 'placeholder' placeholders '"$copts[@]"'
  982. }
  983. (( $+functions[_borg_format_keys] )) ||
  984. _borg_format_keys() {
  985. local repo_or_arch=${(Q)1}
  986. local -a keys=( NEWLINE NL NUL SPACE TAB CR LF )
  987. local -a repository_keys=( archive name barchive comment bcomment id start time end hostname username )
  988. local -a archive_keys=( type mode uid gid user group path bpath source linktarget flags size csize dsize dcsize
  989. num_chunks unique_chunks mtime ctime atime isomtime isoctime isoatime blake2b blake2s md5 sha1 sha224 sha256 sha384
  990. sha3_224 sha3_256 sha3_384 sha3_512 sha512 shake_128 shake_256 archiveid archivename extra health )
  991. local akeys rkeys
  992. akeys='archive-keys:archive keys:compadd -a archive_keys'
  993. rkeys='repository-keys:repository keys:compadd -a repository_keys'
  994. local -a alts=( 'keys:keys:compadd -a keys' )
  995. if [[ $repo_or_arch == *::?* ]]; then
  996. alts+=( $akeys )
  997. elif [[ -n $repo_or_arch ]]; then
  998. alts+=( $rkeys )
  999. else
  1000. alts+=( $rkeys $akeys )
  1001. fi
  1002. __borg_complete_keys _alternative -O copts ${(q)alts}
  1003. }
  1004. (( $+functions[__borg_complete_keys] )) ||
  1005. __borg_complete_keys() {
  1006. compset -P '*[^A-Za-z]##'
  1007. compset -S '[^A-Za-z]##*'
  1008. [[ -n $ISUFFIX ]] && compstate[to_end]=''
  1009. # NOTE: `[[ -n $ISUFFIX ]]` is a workarond for a bug that causes cursor movement to the right further than it should
  1010. # NOTE: the _oldlist completer doesn't respect compstate[to_end]=''
  1011. local ipref suf
  1012. if [[ $IPREFIX[-1] != '{' ]]; then
  1013. ipref='{'
  1014. [[ $compstate[quote] != (\'|\") ]] && ipref='\{'
  1015. fi
  1016. if [[ $ISUFFIX[1] != (|\\)\} ]]; then
  1017. suf='}'
  1018. [[ $compstate[quote] != (\'|\") ]] && suf='\}'
  1019. fi
  1020. local -a copts=( -i "$ipref" -S "$suf" )
  1021. eval "$@"
  1022. }
  1023. # _borg_style_selector_or_archive_files [-e] [-p] archive default_style_selector
  1024. #
  1025. # -e apply exclusion options on the command line
  1026. # -p complete `--pattern`
  1027. # -f complete files rather than borg paths
  1028. (( $+functions[_borg_style_selector_or_archive_files] )) ||
  1029. _borg_style_selector_or_archive_files() {
  1030. local -A opts
  1031. zparseopts -A opts -D -E e p f
  1032. local arch=$1 default_style_selector=$2
  1033. shift 2
  1034. local -a match mbegin mend expl tags=( style-selectors archive-files ) ss_suf=( -S ':' -r ':' )
  1035. (( $+opts[-f] )) && tags=( style-selectors files )
  1036. local -i ret=1
  1037. if (( $+opts[-p] )); then
  1038. if ! compset -P '(#b)([RP\+\-\!])'; then
  1039. local -a pattern_rules=(
  1040. 'P:pattern style'
  1041. 'R:root path'
  1042. '+:include'
  1043. '-:exclude'
  1044. '!:exclude non-recurse'
  1045. )
  1046. _describe -t pattern-rules 'pattern rule' pattern_rules -S ''
  1047. return
  1048. else
  1049. if [[ $compstate[quote] == (\'|\") ]]; then
  1050. compset -P ' #'
  1051. else
  1052. compset -P '(\\ )#'
  1053. fi
  1054. if [[ $match[1] == 'R' ]]; then
  1055. default_style_selector='pp'
  1056. elif [[ $match[1] == 'P' ]]; then
  1057. tags=( style-selectors )
  1058. ss_suf=()
  1059. fi
  1060. fi
  1061. fi
  1062. _tags $tags
  1063. while _tags; do
  1064. if _requested style-selectors; then
  1065. _all_labels style-selectors expl 'style selector' \
  1066. __borg_style_selectors $default_style_selector "$ss_suf[@]" - && ret=0
  1067. fi
  1068. if _requested archive-files; then
  1069. _all_labels archive-files expl 'PATTERN' \
  1070. __borg_archive_files ${(k)opts} "$arch" $default_style_selector - && ret=0
  1071. fi
  1072. if _requested files; then
  1073. local -a borg_paths=( ${(Q)${(e)${~@}}} )
  1074. _all_labels files expl 'PATH' \
  1075. __borg_pattern_files ${(k)opts} borg_paths - && ret=0
  1076. fi
  1077. (( ret )) || return 0
  1078. done
  1079. return 1
  1080. }
  1081. (( $+functions[__borg_style_selectors] )) ||
  1082. __borg_style_selectors() {
  1083. local default_style_selector=$1 path_style_selector
  1084. shift
  1085. zstyle -s ":completion:${curcontext}:archive-files" path-style-selector path_style_selector ||
  1086. path_style_selector='fm'
  1087. local -a disp
  1088. local -A style_selectors
  1089. __borg_setup_style_selectors
  1090. if zstyle -T ":completion:${curcontext}:style-selectors" verbose; then
  1091. local -a style_selector_descriptions extra
  1092. local k v sep
  1093. for k v in ${(kv)style_selectors}; do
  1094. extra=()
  1095. [[ $k == $default_style_selector ]] && extra+=( 'default' )
  1096. [[ $k == $path_style_selector ]] && __borg_choose_path_or_pattern "" "$default_style_selector" &&
  1097. extra+=( 'path' )
  1098. (( $#extra )) && v+=" (${(j:, :)extra})"
  1099. style_selector_descriptions+=( "${${k//\\/\\\\}//:/\\:}:$v" )
  1100. done
  1101. zstyle -s ":completion:${curcontext}:style-selectors" list-separator sep || sep=--
  1102. zformat -a style_selector_descriptions " $sep " $style_selector_descriptions
  1103. disp=( -ld style_selector_descriptions )
  1104. fi
  1105. compadd "$disp[@]" "$@" -k style_selectors
  1106. }
  1107. (( $+functions[__borg_archive_files] )) ||
  1108. __borg_archive_files() {
  1109. local -A opts
  1110. zparseopts -A opts -D e p
  1111. local arch=$1 default_style_selector=$2
  1112. shift 2
  1113. if [[ -z $arch || $arch != *::?* ]]; then
  1114. _message 'no archive specified'
  1115. return 1
  1116. fi
  1117. local -a qargs tmp disp pref match mbegin mend archive_files descs
  1118. local -A style_selectors
  1119. local k cword fmt descfmt style_selector path_style_selector name descr
  1120. # take into account exclude options on the command line
  1121. if (( $+opts[-e] )); then
  1122. local -a exclude_options=( -e --exclude --exclude-from --pattern --pattern-from )
  1123. local -a excludes
  1124. for k in $exclude_options; do
  1125. if [[ -n $opt_args[$k] ]]; then
  1126. IFS=: read -A tmp <<<$opt_args[$k]
  1127. excludes+=( $k="${^tmp[@]}" )
  1128. fi
  1129. done
  1130. [[ -n $excludes ]] && qargs+=( "$excludes[@]" )
  1131. fi
  1132. (( $_matcher_num > 1 )) && return 1
  1133. __borg_skip_pattern_matching || return 1
  1134. cword="$PREFIX$SUFFIX"
  1135. [[ $compstate[quote] != (\'|\") ]] && cword=${(Q)cword}
  1136. [[ -z $cword ]] && return 1
  1137. if zstyle -t ":completion:${curcontext}:archive-files" verbose; then
  1138. zstyle -s ":completion:${curcontext}:archive-files" file-description-format descfmt ||
  1139. descfmt='{mode} {user:6} {group:6} {size:8d} {mtime} {path}{extra}'
  1140. fmt="{bpath}{NUL}$descfmt{NUL}"
  1141. else
  1142. fmt='{bpath}{NUL}'
  1143. fi
  1144. qargs+=( --format=${(q)fmt} )
  1145. qargs+=( $arch )
  1146. __borg_setup_style_selectors
  1147. [[ $cword == (#b)(${~${(j:|:)${(kb)style_selectors}}}):* ]] && style_selector=$match[1]
  1148. local -i path_expected=0
  1149. __borg_choose_path_or_pattern "$style_selector" $default_style_selector $cword && path_expected=1
  1150. if [[ -n $cword ]]; then
  1151. if (( path_expected )); then
  1152. [[ -n $style_selector ]] && compset -P "$style_selector:" && pref=( -P "$style_selector:" )
  1153. cword="$PREFIX$SUFFIX"
  1154. [[ $compstate[quote] != (\'|\") ]] && cword=${(Q)cword}
  1155. zstyle -s ":completion:${curcontext}:archive-files" path-style-selector path_style_selector ||
  1156. path_style_selector='fm'
  1157. cword="$path_style_selector:$cword"
  1158. else
  1159. [[ -z $style_selector ]] && cword="$default_style_selector:$cword"
  1160. fi
  1161. qargs+=( ${(q)cword} )
  1162. fi
  1163. if zstyle -t ":completion:${curcontext}:archive-files" verbose; then
  1164. _call_program -p archive-file-descriptions ${(q)__borg_command:-borg} list $qargs 2>/dev/null |
  1165. while IFS= read -r -d $'\0' name && IFS= read -r -d $'\0' descr; do
  1166. archive_files+=( $name )
  1167. descs+=( $descr )
  1168. done
  1169. (( $pipestatus[1] )) && { _message "couldn't list archive: ${(Q)arch}"; return 1 }
  1170. disp=( -ld descs )
  1171. else
  1172. archive_files=( ${(0)"$(_call_program -p archive-files ${(q)__borg_command:-borg} list $qargs 2>/dev/null)"} )
  1173. (( $pipestatus[1] )) && { _message "couldn't list archive: ${(Q)arch}"; return 1 }
  1174. fi
  1175. if (( $#archive_files )); then
  1176. if (( path_expected )); then
  1177. compstate[insert]='automenu'
  1178. else
  1179. compstate[insert]=''
  1180. compstate[list]='list force'
  1181. fi
  1182. fi
  1183. compadd "$pref[@]" -U "$disp[@]" "$@" -a archive_files
  1184. }
  1185. (( $+functions[__borg_choose_path_or_pattern] )) ||
  1186. __borg_choose_path_or_pattern() {
  1187. local ss=$1 defss=$2 cword=$3
  1188. shift 2
  1189. [[ $ss == (pp|pf) || ( -z $ss && $defss == (pp|pf) ) ]]
  1190. }
  1191. # transform borg exclude patterns into zsh ignore patterns and then complete files
  1192. (( $+functions[__borg_pattern_files] )) ||
  1193. __borg_pattern_files() {
  1194. local -A opts
  1195. zparseopts -A opts -D -E e p f
  1196. local paths_varname=$1
  1197. shift
  1198. local -a args
  1199. local -A style_selectors
  1200. __borg_setup_style_selectors
  1201. local pr_pat='[RP\+\-\!]' ss_pat="(${(j:|:)${(@kb)style_selectors}}):"
  1202. local prs_pat="$pr_pat #"
  1203. if (( $+opts[-e] )); then
  1204. local -a borg_excludes exclude_options=( -e --exclude --pattern ) tmp
  1205. local k cword
  1206. local -i i
  1207. for k in $exclude_options; do
  1208. if [[ -n $opt_args[$k] ]]; then
  1209. IFS=: read -A tmp <<<$opt_args[$k]
  1210. tmp=( ${(Q)tmp} )
  1211. # lstrip style selectors and pattern rules
  1212. [[ $+opts[-p] -gt 0 || $k == --pattern ]] && tmp=( ${tmp#$~prs_pat} )
  1213. tmp=( ${tmp#$~ss_pat} )
  1214. # don't take into account the word under the cursor
  1215. cword="$PREFIX$SUFFIX"
  1216. [[ $compstate[quote] != (\'|\") ]] && cword=${(Q)cword}
  1217. [[ $+opts[-p] -gt 0 || $k == --pattern ]] && cword=${cword#$~prs_pat}
  1218. cword=${cword#$~ss_pat}
  1219. i=$tmp[(I)$cword]
  1220. (( i )) && tmp=( "${(@)tmp[1,i-1]}" "${(@)tmp[i+1,-1]}" )
  1221. borg_excludes+=( "$tmp[@]" )
  1222. fi
  1223. done
  1224. [[ -n $borg_excludes ]] && args+=( -F borg_excludes )
  1225. fi
  1226. [[ -n ${(P)paths_varname} ]] && args+=( -W $paths_varname )
  1227. args+=( "$@" )
  1228. # lstrip style selectors and pattern rules
  1229. if (( $+opts[-p] )); then
  1230. if [[ $compstate[quote] != (\'|\") ]]; then
  1231. compset -P $pr_pat
  1232. compset -P '(\\ )#'
  1233. else
  1234. compset -P $prs_pat
  1235. fi
  1236. fi
  1237. compset -P $ss_pat
  1238. compstate[insert]=''
  1239. compstate[list]='list force'
  1240. _path_files "$args[@]"
  1241. }
  1242. (( $+functions[__borg_setup_style_selectors] )) ||
  1243. __borg_setup_style_selectors() {
  1244. typeset -gA style_selectors=(
  1245. fm 'Fnmatch'
  1246. sh 'Shell-style patterns'
  1247. re 'Regular expressions'
  1248. pp 'Path prefix'
  1249. pf 'Path full-match'
  1250. )
  1251. }
  1252. (( $+functions[__borg_skip_pattern_matching] )) ||
  1253. __borg_skip_pattern_matching() {
  1254. # unset glob_complete
  1255. [[ $compstate[pattern_match] == '*' ]] && compstate[pattern_match]=''
  1256. # skip the _match completer
  1257. [[ -n $compstate[pattern_match] ]] && return 1
  1258. return 0
  1259. }
  1260. (( $+functions[_borg_config] )) ||
  1261. _borg_config() {
  1262. local qrepo=$1
  1263. shift
  1264. if (( ! $+__borg_config_sect )); then
  1265. comppostfuncs+=( __borg_unset_config )
  1266. typeset -gH __borg_config_sect=
  1267. typeset -gHa __borg_config_keys=()
  1268. local sect line
  1269. local -a match mbegin mend
  1270. _call_program -p keys ${(q)__borg_command:-borg} config --list $qrepo 2>/dev/null | {
  1271. IFS= read -r sect
  1272. sect=${${sect#\[}%\]}
  1273. __borg_config_sect=$sect
  1274. while IFS= read -r line && [[ $line == (#b)(*)\ =\ (*) ]]; do
  1275. __borg_config_keys+=( "${${match[1]//\\/\\\\}//:/\\:}:(current: $match[2])" )
  1276. done
  1277. }
  1278. fi
  1279. local -a alts=( 'keys:key: _describe -t keys "key" __borg_config_keys' )
  1280. compset -P "${__borg_config_sect}." || alts+=( 'sections:section:compadd -S "." $__borg_config_sect' )
  1281. _alternative $alts
  1282. }
  1283. (( $+functions[__borg_unset_config] )) ||
  1284. __borg_unset_config() {
  1285. unset __borg_config_sect __borg_config_keys
  1286. }
  1287. # A simple prefix-oriented completion function for compressors. Can be improved by supporting the suffix.
  1288. (( $+functions[_borg_compression] )) ||
  1289. _borg_compression() {
  1290. local -a nolvl=(
  1291. 'none:do not compress'
  1292. 'lz4:very high speed, very low compression'
  1293. )
  1294. local -a havelvl=(
  1295. 'zstd:("zstandart")'
  1296. 'zlib:("gz") medium speed, medium compression'
  1297. 'lzma:("xz") low speed, high compression'
  1298. )
  1299. local -a auto=(
  1300. 'auto:compress compressible, otherwise "none"'
  1301. )
  1302. local -a match mbegin mend
  1303. # NOTE: Zsh's `-prefix` condition is confused by the leading parenthesis in the pattern.
  1304. # Fortunately, we simply need to show a message.
  1305. if compset -P '(#b)(|auto,)(zstd|zlib|lzma),'; then
  1306. local -i from to def
  1307. case $match[2] in
  1308. (zstd) from=1 to=22 def=3 ;;
  1309. (zlib|lzma) from=0 to=9 def=6 ;;
  1310. esac
  1311. _message -e "compression level (from $from to $to, default: $def)"
  1312. elif compset -P 'auto,'; then
  1313. _describe -t compression 'compression' nolvl -- havelvl -qS,
  1314. else
  1315. _describe -t compression 'compression' nolvl -- havelvl -qS, -- auto -S,
  1316. fi
  1317. }
  1318. (( $+functions[_borg_chunker_params] )) ||
  1319. _borg_chunker_params() {
  1320. if compset -P '*,*,*,'; then
  1321. _message -e 'HASH_WINDOW_SIZE'
  1322. elif compset -P '*,*,'; then
  1323. _message -e 'HASH_MASK_BITS (statistical medium chunk size ~= 2^HASH_MASK_BITS B)'
  1324. elif compset -P '*,'; then
  1325. _message -e 'CHUNK_MAX_EXP (maximum chunk size = 2^CHUNK_MAX_EXP B)'
  1326. else
  1327. _message -e 'CHUNK_MIN_EXP (minimum chunk size = 2^CHUNK_MIN_EXP B)'
  1328. local -a params=(
  1329. 'default:19,23,21,4095'
  1330. '19,23,21,4095:small amount of chunks (default)'
  1331. '10,23,16,4095:big amount of chunks'
  1332. )
  1333. _describe -t chunker-params 'typical chunker params' params
  1334. fi
  1335. }
  1336. (( $+functions[_borg_statuschars] )) ||
  1337. _borg_statuschars() {
  1338. _values -s '' 'STATUSCHARS' \
  1339. 'A[regular file, added]' \
  1340. 'M[regular file, modified]' \
  1341. 'U[regular file, unchanged]' \
  1342. 'E[regular file, an error happened while accessing/reading this file]' \
  1343. 'd[directory]' \
  1344. 'b[block device]' \
  1345. 'c[char device]' \
  1346. 'h[regular file, hardlink (to already seen inodes)]' \
  1347. 's[symlink]' \
  1348. 'f[fifo]' \
  1349. 'i[backup data was read from standard input (stdin)]' \
  1350. '-[dry run, item was not backed up]' \
  1351. 'x[excluded, item was not backed up]' \
  1352. '?[missing status code]'
  1353. }
  1354. (( $+functions[_borg_quota_suffixes] )) ||
  1355. _borg_quota_suffixes() {
  1356. if compset -P '[0-9]##'; then
  1357. local -a suffixes=(
  1358. 'K:10 ** 3 bytes'
  1359. 'M:10 ** 6 bytes'
  1360. 'G:10 ** 9 bytes'
  1361. 'T:10 ** 12 bytes'
  1362. 'P:10 ** 15 bytes'
  1363. )
  1364. # NOTE: tag `suffixes` is already in use (file extensions)
  1365. _describe -t multiplier 'suffix' suffixes
  1366. else
  1367. _message -e 'QUOTA'
  1368. fi
  1369. }
  1370. (( $+functions[_borg_timestamp] )) ||
  1371. _borg_timestamp() {
  1372. _alternative \
  1373. "dates:TIMESTAMP: _dates -f '%FT%T'" \
  1374. 'files:reference:_files'
  1375. }
  1376. (( $+functions[_borg_guard_unsigned_number] )) ||
  1377. _borg_guard_unsigned_number() {
  1378. local -A opts
  1379. zparseopts -K -D -A opts M+: J+: V+: 1 2 o+: n F: x+: X+:
  1380. _guard '[0-9]#' ${1:-number}
  1381. }
  1382. _borg() {
  1383. local -a match mbegin mend line state
  1384. local curcontext="$curcontext" state_descr
  1385. typeset -A opt_args
  1386. local -i ret=1
  1387. if [[ $service == 'borg' ]]; then
  1388. local __borg_command=$words[1]
  1389. local -a common_options
  1390. __borg_setup_common_options
  1391. _arguments -s -w -C : \
  1392. '(- :)'{-V,--version}'[show version number and exit]' \
  1393. $common_options \
  1394. '(-): :->command' \
  1395. '(-)*:: :->option-or-argument' && return
  1396. case $state in
  1397. (command)
  1398. _borg_commands && ret=0
  1399. ;;
  1400. (option-or-argument)
  1401. curcontext="${curcontext%:*:*}:borg-$words[1]:"
  1402. if ! _call_function ret _borg-$words[1]; then
  1403. _default && ret=0
  1404. fi
  1405. ;;
  1406. esac
  1407. elif [[ $service == (#b)-value-,BORG_(*),-default- ]]; then
  1408. _borg_parameters $match[1] && ret=0
  1409. elif ! _call_function ret _$service; then
  1410. _default && ret=0
  1411. fi
  1412. return ret
  1413. }
  1414. _borg "$@"