_borg 54 KB

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