1#compdef rpm rpmbuild rpmkeys rpmsign rpmspec rpmquery rpmverify
2
3# This uses `_arguments' in a state-machine kind of way. These states
4# have names and before executing the default action for such a state
5# we try to call a function with the name `_rpm_<state>'. If such a
6# function exists, we return with its return status immediately. This
7# allows users to override the default completions by simply defining
8# these functions.
9# The states (and possible values for the `<state>' above) are:
10#
11#  common
12#    complete for basic options like --querytags and --showrc
13#  query
14#    complete for `rpm -q' query
15#  verify
16#    complete for `rpm --verify'
17#  install
18#    complete for `rpm -i' or `rpm --install'
19#  upgrade
20#    complete for `rpm -U' or `rpm --upgrade'
21#  uninstall
22#    complete for `rpm -e' or `rpm --erase'
23#  build_b
24#    complete for `rpmbuild -bx' (the stage `x' is already completed)
25#  build_r
26#    complete for `rpmbuild -rx' (the stage `x' is already completed)
27#  build_t
28#    complete for `rpmbuild -tx' (the stage `x' is already completed)
29#  checksig
30#    complete for `rpm --checksig'
31#  package
32#    complete a RPM package name
33#  package_file
34#    complete a RPM package file name
35#  file_or_package
36#    an absolute path to any file (not a package file) or a package
37#  tags
38#    complete a tag name
39#  capability
40#    complete a capability
41#  relocate
42#    complete a `old=new' pair of paths
43#  setattrs
44#    complete for --setperms, --setugids, --setcaps and --restore
45#  public_keys
46#    complete for `rpmkeys --import'
47#  query_specs
48#    complete for `rpmspec --query'
49
50_rpm () {
51  local curcontext="$curcontext" state lstate line nm="$compstate[nmatches]"
52  typeset -A opt_args
53  local ret
54  local -a tmp expl opts commonopts selectopts fileopts pathopts buildopts queryopts
55
56  commonopts=(
57    '(-v --verbose)--quiet[print as little as possible]'
58    '(--quiet)*'{-v,--verbose}'[verbose output]'
59    '--rcfile=:configuration file:_sequence -s \: _files'
60    '--httpproxy=:http proxy server:_hosts'
61    '--httpport=:http port number'
62    '--pipe=[pipes the output of rpm to the specified command]:pipe command:_cmdstring'
63    \*{-D,--define=}'[define a macro]:macro value'
64    '*--undefine=[undefine a macro]:macro:->macros'
65    '--target=[specify target platform]:arch-vendor-os'
66    '--macros=[read macros from specified files instead of the defaults]:file:_sequence -s \: _files'
67    '--load=[load a single macro file]:file:_files'
68    "--noplugins[don't enable any plugins]"
69    "--nodigest[don't verify package digest(s)]"
70    "--nosignature[don't verify package signature(s)]"
71  )
72
73  # package selection options of which only one can be used
74  selectopts=(
75    {-a,--all}'[query all packages]'
76    {-f,--file}'[query packages that own specified files]'
77    {-p,--package}'[query uninstalled packages]'
78    {-g,--group}'[query packages in one of specified groups]'
79    --pkgid --hdrid --tid --querybynumber
80    '--triggeredby'
81    '--whatconflicts'
82    '--whatrequires'
83    '--whatobsoletes'
84    '--whatprovides'
85    '--whatrecommends'
86    '--whatsuggests'
87    '--whatsupplements'
88    '--whatenhances'
89    '--nomanifest'
90  )
91  sopts=${selectopts%\[*}\ --specfile
92  selectopts=(
93    "(* $sopts)"${selectopts[1,2]}
94    "($sopts)"${selectopts[3,-1]}
95    '(-a --all)*: :->package-select'
96  )
97
98  fileopts=(
99    '(-c --configfiles)'{-c,--configfiles}'[configuration files only]'
100    '(-d --docfiles)'{-d,--docfiles}'[documentation files only]'
101    '(-L --licensefiles)'{-L,--licensefiles}'[license files only]'
102    '(-A --artifactfiles)'{-A,--artifactfiles}'[artifact files only]'
103    '--noghost[exclude ghost files]'
104    '--noconfig[exclude config files]'
105    '--noartifact[exclude artifact files]'
106  )
107
108  pathopts=(
109    {-r,--root=}'[specify rpm root directory]:directory:_directories'
110    '--dbpath=[specify rpm database path]:path:_directories'
111  )
112
113  buildopts=(
114    '--rpmfcdebug[debug dependencies generation]'
115    '--buildroot=[override the build root]:build root directory:_directories'
116    '--build-in-place[run build in current directory]'
117    '--clean[remove the build tree after the packages are made]'
118    "--nobuild[don't execute any stages of the build]"
119    '--nodeps[do not verify build dependencies]'
120    '--nodirtokens[generate package header(s) compatible with (legacy) rpm v3 packaging]'
121    "--noclean[don't execute %clean stage of the build]"
122    "--noprep[don't execute %prep stage of the build]"
123    "--nocheck[don't execute %check stage of the build]"
124    '--rmsource[remove sources when done]'
125    '--rmspec[remove the spec file when done]'
126    '--short-circuit[skip straight to specified stage (only for c,i)]'
127    '*--with=[enable configure option for build]:option'
128    '*--without=[disable configure option for build]:option'
129    '--scm=[select the SCM to use with %autosetup]:scm [patch]:(patch gendiff git quilt)'
130    '*--buildpolicy=[set buildroot policy]:policy:->brp_policies'
131    '!--sign'
132    "--nodebuginfo[don't generate debuginfo for this package]"
133  )
134
135  queryopts=(
136    '--conflicts'
137    '--obsoletes[list packages obsoleted by package]'
138    '--provides[list capabilities provided by package]'
139    '(-R)--requires[list capabilities on which packages depend]'
140  )
141
142  case $service in
143    rpm|rpmkeys)
144      opts+=(
145        '--import[import an armored public key]:*: :->public_keys'
146        {-K,--checksig}'[signature check mode]:*:sigcheck:->checksig'
147      )
148    ;|
149    rpm|rpmdb)
150      opts+=( --{init,rebuild}'db:*: :->common' )
151    ;|
152    rpm|rpmsign)
153      opts+=(
154        --{add,re}sign'[sign package(s)]:*: :->sign'
155        '--delsign[delete package signatures]:*: :->sign'
156      )
157    ;|
158    rpm)
159      opts+=(
160        {-q+,--query}'[query mode]:*:query:->query'
161        {-V+,-y+,--verify}'[verify mode]:*:verify:->verify'
162        {-i+,--install}'[install mode]:*:install:->install'
163        {-U+,--upgrade}'[upgrade mode]:*:upgrade:->upgrade'
164        {-F+,--freshen}'[freshen mode]:*:upgrade:->upgrade'
165        {-e+,--erase}'[uninstall mode]:*:uninstall:->uninstall'
166        '--reinstall[reinstall mode]:*:install:->install'
167        '--setperms[set file permissions]:*:package:->setattrs'
168        '--setugids[set file owner/group]:*:package:->setattrs'
169        '--setcaps[set capabilities of files in the given package]:*:package:->setattrs'
170        '--restore[restore owner, group, permissions and capabilities of files in the given package]:*:package:->setattrs'
171      )
172    ;;
173    rpmbuild)
174      [[ -prefix -r ]] && pathopts[1]=
175      opts+=( $buildopts
176        '(-r -t)-b+[build mode (spec file)]:build stage:((p\:execute\ \%prep\ stage l\:do\ a\ list\ check c\:execute\ build\ stage i\:execute\ install\ stage b\:build\ a\ binary\ package a\:build\ binary\ and\ source\ packages s\:build\ source\ package\ only r\:build\ source\ package\ only\ -\ calculate\ build\ requires)):*:build:->build_b'
177        '(-b -t)-r+[build mode (source package)]:build stage:((p\:execute\ \%prep\ stage l\:do\ a\ list\ check c\:execute\ build\ stage i\:execute\ install\ stage b\:build\ a\ binary\ package a\:build\ binary\ and\ source\ packages s\:build\ source\ package\ only r\:build\ source\ package\ only\ -\ calculate\ build\ requires)):*:build:->build_b'
178        '(-b -r)-t+[build mode (tar file)]:build stage:((p\:execute\ \%prep\ stage l\:do\ a\ list\ check c\:execute\ build\ stage i\:execute\ install\ stage b\:build\ a\ binary\ package a\:build\ binary\ and\ source\ packages s\:build\ source\ package\ only r\:build\ source\ package\ only\ -\ calculate\ build\ requires)):*:build:->build_t'
179        --{rebuild,recompile}':*:source rpm file:->build_r'
180      )
181    ;;
182    rpmspec)
183      opts+=(
184        {-P,--parse}'[parse spec files]:*: :->spec_files'
185        {-q,--query}'[query spec files]:*: :->query_specs'
186      )
187    ;;
188    rpmquery) state=query ;;
189    rpmverify) state=verify ;;
190  esac
191
192  [[ -z $state ]] && _arguments -C -s $pathopts $opts $commonopts \
193    '(-)'{-\?,--help}'[print help information]' \
194    '(-)--usage[print brief usage message]' \
195    '(-)--version[print version number]' \
196    \*{-E,--eval=}'[print macro expansion of given expression]:expression:->tags' \
197    --{querytags,showrc}':*: :->common'
198
199  # As long as we have a state name...
200
201  while [[ -n "$state" ]]; do
202
203    # First try to call a user-defined function.
204
205    _call_function ret _rpm_$state && return ret
206
207    # Copy the state and reset `state', to simplify the test above.
208
209    lstate="$state"
210    state=''
211    tmp=()
212
213    # Dispatch...
214
215    case "$lstate" in
216    common)
217      _arguments -s -C $commonopts
218    ;;
219    query)
220      # --dump requires one of -{l,c,d}
221      # --triggers requires --script
222      _arguments -s -C \
223        \!{-q,--query} $commonopts $selectopts $fileopts $pathopts $queryopts \
224        '--dump[dump basic file information]' \
225        \*--{qf,queryformat}'[specify format for package information]:rpm query format:->tags' \
226        "($sopts)--specfile[query specified spec file as if it were a package]" \
227        '(-i --info)'{-i,--info}'[display package information]' \
228        '--changelog' '--changes' '--dupes' \
229        '--last[order packages by install time]' \
230        '--xml' \
231        '--recommends[list capabilities recommended by packages]' \
232        '(--requires)-R[list capabilities on which packages depend]' \
233        '--suggests[list capabilities suggested by packages]' \
234        '--supplements[list capabilities supplemented by packages]' \
235        '(-s --state -l --list --filesbypkg)'{-l,--list}'[list files in package]' \
236        '(-s --state -l --list)'{-s,--state}'[show file states]' \
237        '--fileclass' '--filecolor' '--fileprovide' '--filerequire' \
238        '--filecaps' '--filesbypkg[list files with package names]' \
239        '--filetriggers[list filetrigger scriptlets]' \
240        '--scripts[show (un)install scripts]' \
241        {--triggers,--triggerscripts}'[show trigger scripts]'
242      ;;
243    query_specs)
244      _arguments -s -C \
245        \!{-q,--query} $commonopts $pathopts $queryopts \
246        --buildconflicts --buildrequires --trace \
247        '*:spec file:->spec_files'
248    ;;
249    setattrs)
250      _arguments -s -C '!--set{perm,ugids,caps}' '!--restore' $selectopts
251      ;;
252    verify)
253      _arguments -s -C \
254        \!{-V,--verify} $commonopts $selectopts $fileopts $pathopts \
255        --no{deps,digest,files,scripts,signature,linkto,filedigest,size,user,group,mtime,mode,rdev,caps}
256      ;;
257    upgrade)
258      tmp=( '(--force)--oldpackage' )
259      ;&
260    install)
261      _arguments -s -C \!{-i,--install,-U,--upgrade,-F,--freshen} $tmp \
262        $commonopts $pathopts \
263        '--excludepath=:file to exclude:_files -/' \
264	'--relocate:relocate:->relocate' \
265        '--prefix=[relocate the package]:package prefix directory:_files -/' \
266        '(-h --hash)'{-h,--hash}'[print hash marks as package installs]' \
267	'(--replacepkgs --replacefiles --oldpackage)--force' \
268	'(--force)--'{replacefiles,replacepkgs} \
269        --{aid,allfiles,badreloc,excludedocs,ignorearch,ignoreos,ignoresize,includedocs,justdb,percent,test} \
270        --no{deps,filedigest,contexts,caps,order,suggest,pre,post,preun,postun,trigger{s,in,un,postun}} \
271	'(--nopre --nopost --nopreun --nopostun)--noscripts' \
272        '*:pkg file:->package_file'
273      ;;
274    uninstall)
275      _arguments -s -C \!{-e,--erase} \
276	"${commonopts[@]}" "${pathopts[@]}" \
277	--{allmatches,justdb,repackage,test} \
278	--no{deps,scripts,preun,postun,trigger{s,un,postun}} \
279        '*:package:->package'
280      ;;
281    build_b) tmp=( '*:spec file:_files -g "*.spec(-.)"' ) ;|
282    build_r) tmp=( '*:source package:_files -g "*.(#i)src.rpm(-.)"' ) ;|
283    build_t) tmp=( '*:tar file:_files -g "*.(#i)tar(.*|)(-.)"' ) ;|
284    build_?)
285      _arguments -s -C $buildopts $commonopts $pathopts \
286      ;;
287    checksig)
288      _arguments -s -C \!-K \
289	"${commonopts[@]}" \
290        --no{gpg,pgp,md5,digest} \
291        '*:package file:->package_file'
292      ;;
293    sign)
294      _arguments -s -C $commonopts $pathopts \
295        '--signfiles[sign package(s) files]' \
296        '--fskpath=[use file signing key]:key:_files' \
297        '--fskpass[prompt for file signing key password]' \
298        '--key-id=[specify key id/name to sign with]:key id' \
299        '--digest-algo=[override default digest algorithm]:algorithm:(sha1 sha256 sha384 sha512)' \
300        '*:package file:_files -g "*.(#i)rpm(-.)"'
301    ;;
302    package-select)
303      case "${opt_args[(i)${sopts// /|}]}" in
304	-f|--file) _files ;;
305	-p|--package) state=package_file ;;
306	-g|--group) state=groups ;;
307	--fileid|--pkgid) _message -e md5 md5 ;;
308	--hdrid) _message -e sha1 sha1 ;;
309	--querybynumber) _message -e value number ;;
310	--tid) _message -e ids 'transaction id' ;;
311	--what*) state=capabilities ;;
312	--specfile) state=spec_files ;;
313	*) state=package ;;
314     esac
315    ;;
316    macros)
317      local -a macros
318      macros=( ${${${(M)${(f)"$(_call_program macros rpm --showrc)"}:#(-|)[0-9]##[:=] ##*}#* }%%[[:blank:](]*} )
319      _description macros expl macro
320      if zstyle -t ":completion:${curcontext}:macros" prefix-hidden; then
321        compadd "$expl[@]" -p '%' -a - macros
322      else
323        macros=( %${^macros} )
324        compadd "$expl[@]" -a - macros
325      fi
326    ;;
327    target)
328      _wanted targets expl 'target platform' compadd \
329          ${${(M)${(f)"$(_call_programs targets rpm --showrc)"}:#compatible archs*}##*: }
330      ;;
331    groups)
332      if ( (( ! $+_rpm_groups )) || _cache_invalid rpm-groups ) &&
333	  ! _retrieve_cache rpm-groups
334      then
335	typeset -gaU _rpm_groups
336	_rpm_groups=(
337	    ${(f)"$(_call_program groups rpm -qa --queryformat '%\{group}\\n' 2>/dev/null)"}
338	)
339	_store_cache RPM-groups _rpm_groups
340      fi
341      _wanted groups expl 'group' _multi_parts / _rpm_groups
342    ;;
343    file_or_package)
344      if [[ $PREFIX = /* ]]; then
345	_wanted files expl 'file' _files
346      else
347	state=package
348      fi
349      ;;
350    package)
351      if ( [[ ${+_rpms} -eq 0 ]] || _cache_invalid RPMs ) &&
352	 ! _retrieve_cache RPMs;
353      then
354        _rpms=( $(_call_program packages rpm -qa) )
355	_store_cache RPMs _rpms
356      fi
357      _wanted packages expl 'package' \
358          compadd -M 'r:|-=* r:|=*' - "$_rpms[@]"
359      ;;
360    spec_files)
361      _wanted specfiles expl 'spec file' \
362          _files -g '*.spec(-.)'
363      ;;
364    package_file)
365      _wanted files expl 'package file' \
366          _files -g '*.(#i)rpm(-.)'
367      if [[ -prefix 1 (f|ht)tp:// ]]; then
368	_wanted urls expl 'URL of rpm package file' \
369            _urls -f -g '*.(#i)rpm(-.)' "${expl[@]}"
370      else
371	_wanted urls expl 'URL of rpm package file' \
372            compadd -S '' "${expl[@]}" ftp:// http://
373      fi
374      ;;
375    package_src)
376      _wanted files expl 'source package' _files -g '(#i)*.src.rpm(-.)'
377      ;;
378    tags)
379      local -a suf
380      if compset -P "*%*${${QIPREFIX:+{}:-\{}"; then
381        compset -S '(|\\)}*' || suf=( -S ${${QIPREFIX:+\}}:-\\\}} -r ": \}\t\n\-" )
382        if compset -P '*:'; then
383          _wanted formats expl format compadd $suf - \
384              armor arraysize base64 date day depflags deptype expand \
385              fflags fstate fstatus hex octal humaniec humansi perms \
386              pgpsig shescape triggertype vflags xml
387        else
388          _wanted tags expl 'rpm tag' compadd -M 'm:{a-z}={A-Z}' "$suf[@]" - \
389              "${(L@)${(@f)$(_call_program tags rpm --querytags)}#RPMTAG_}"
390        fi
391      else
392        _message -e formats 'rpm query format'
393      fi
394      ;;
395    capabilities)
396      local match mbegin mend
397      if [[ "${opt_args[(i)${sopts// /|}]}" = --what(#b)(*) ]]; then
398        if [[ $match[1] = provides && -prefix / ]]; then
399          _wanted files expl file _files
400        else
401          _description capabilities expl capability
402          compadd ${${(f)"$(_call_program capabilities rpm -qa --queryformat '%\{$match[1]}\\n')"}:#\(none\)}
403        fi
404      fi
405      ;;
406    relocate)
407      if compset -P 1 '*='; then
408	_description directories expl 'new path'
409      else
410	_description directories expl 'old path'
411      fi
412
413      _files "$expl[@]" -/
414      ;;
415    public_keys)
416      _arguments -s -C \!--import $commonopts \
417        '*:public key:_files'
418    ;;
419    brp_policies)
420      local rpmconfigdir=$(_call_program policies rpm -E '%\{_rpmconfigdir\}')
421      _wanted policies expl policy compadd $rpmconfigdir/brp-*(N:t:s/brp-//)
422    ;;
423    esac
424
425    [[ $nm -ne $compstate[nmatches] ]] && return 0
426  done
427
428  return ret
429}
430
431# set a sensible default caching policy
432local update_policy
433zstyle -s ":completion:*:*:rpm:*" cache-policy update_policy
434if [[ -z "$update_policy" ]]; then
435  zstyle ":completion:*:*:rpm:*" cache-policy _rpms_caching_policy
436fi
437
438_rpms_caching_policy () {
439  # rebuild if cache is more than a week old
440  local -a oldp
441  oldp=( "$1"(mw+1) )
442  (( $#oldp )) && return 0
443
444  pkg_indices=( /var/lib/rpm/{packages.rpm,Packages}(N) )
445  for pkg_index in $pkg_indices; do
446    [[ "$pkg_index" -nt "$1" ]] && return 0
447  done
448}
449
450_rpm "$@"
451