1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
|
#compdef opkg ipkg
# Notes:
#
# - This function has been designed with opkg in mind, but much of it should
# also work with ipkg.
#
# - Caching doesn't appear to save a HUGE amount of time given the scale of most
# opkg repos (compared to e.g. APT) and the resources available to the devices
# that use them.
#
# - _opkg_pkg_* functions can be called with --update to update their respective
# cache files without actually completing.
#
# - Lots of code redundancy here (@todo).
#
# Notable styles supported:
#
# % zstyle ':completion:*:opkg:*' use-cache <yes/no>
# Set to yes to enable caching of package names. Usually disabled by default.
#
# % zstyle ':completion:*:opkg:*' cache-path <directory>
# Set to a directory path to override the default cache-file directory.
#
# % zstyle ':completion:*:opkg:*' cache-persists <yes/no>
# Set to yes to keep cache data in memory for the remainder of the shell
# session. Most completion functions do this always, but opkg tends to be used
# on fairly resource-constrained devices, so it's disabled by default here.
#
# % zstyle ':completion:*:opkg:*' status-paths <pattern> ...
# Set to one or more paths or glob patterns to override the defaults used when
# checking cache validity. If any of the specified files has been modified
# more recently than the cache, the cache is considered invalid.
#
# % zstyle ':completion:*:opkg:*' conf-paths <pattern> ...
# Set to one or more paths or glob patterns to override the defaults used when
# searching opkg configuration data.
#
# Elevated privileges may be necessary to complete package names, etc.; consider
# setting the gain-privileges style as follows:
# zstyle ':completion:*:(ipkg|opkg)/*' gain-privileges yes
##
# Check cache validity.
__opkg_cache_policy() {
local -a tmp
# Always invalidate if it's been over a week
tmp=( $1(#qmw+1N) )
(( $#tmp )) && return 0
zstyle -a ":completion:$curcontext:" status-paths tmp
if (( $#tmp )); then
tmp=( $~tmp(#qN) )
else
tmp=(
{/opt,/usr,/var}/lib/{i,o}pkg/status(#q-.N)
{/opt,/usr,/var}/lib/{i,o}pkg/lists/packages(#q-.N)
/opt/var/opkg-lists/packages(#q-.N)
)
fi
# Always invalidate if we found no status files
(( $#tmp )) || return 0
# Invalidate if any status file is newer than the cache file
for 2 in $tmp; do
[[ $2 -nt $1 ]] && return 0
done
return 1
}
##
# Search opkg config files.
__opkg_grep_conf() {
local -aU tmp
zstyle -a ":completion:$curcontext:" conf-paths tmp
if (( $#tmp )); then
tmp=( $~tmp(#qN) )
else
tmp=(
{,/opt}/etc/{i,o}pkg*.conf(#q-.N)
{,/opt}/etc/{i,o}pkg/*.conf(#q-.N)
)
fi
(( $#tmp )) || return 1
GREP_OPTIONS= command grep -sE "$@" $tmp
}
##
# Complete architecture/priority pair.
#
# Architecture names are essentially arbitrary (up to the packager), so we can't
# really complete every possibility here — but we'll account for most of the
# popular ones.
_opkg_arch_prio() {
local -a copts=( "$@" )
local -aU tmp
[[ -prefix *: ]] && {
_message priority
return
}
# Already configured arches
tmp=( ${(f)"$( _call_program -p architectures $svc print-architecture )"} )
tmp=( ${${tmp##arch[ ]##}%% *} )
tmp+=(
# 'Meta' arches
all any noarch
# Arches supported by entware-ng
armv5soft armv7soft mipselsf x86-32 x86-64
# Arches mentioned in the optware-ng source
arm armeb fsg3be hpmv2 i686 ixp4xxbe ixp4xxle mssii nslu2 powerpc qemux86
slugosbe slugosle
# Arches mentioned in the Ångström distribution's narcissus source
a780 ac100 akita am180x-evm am3517-crane am3517-evm am37x-evm archos5
archos5it arm arm-oabi armeb armv4 armv4b armv4t armv4tb armv5 armv5-vfp
armv5e armv5e-vfp armv5eb armv5t armv5t-vfp armv5te armv5te-vfp armv5teb
armv6 armv6-vfp armv6t-vfp armv7 armv7-vfp armv7a armv7a-vfp armv7a-vfp-neon
armv7at2-vfp armv7at2-vfp-neon armv7t2-vfp at32stk1000 at91sam9263ek
atngw100 avr32 beagleboard beaglebone bug20 c6a816x-evm c6a816x_evm c7x0
cm-t35 collie da830-omapl137-evm da850-omapl138-evm davinci-dvevm dht-walnut
dm355-evm dm355-leopard dm357-evm dm365-evm dm3730-am3715-evm dm37x-evm
dm6446-evm dm6467-evm dm6467t-evm dns323 eee701 efika h2200 h3900 h4000
h5000 hawkboard htcalpine hx4700 i386 i486 i586 i686 igep0020 iwmmxt
ixp4xxbe ixp4xxle kuropro lsppchd lsppchg lspro mini2440 mini6410 mips
mv2120 n1200 n2100 neuros-osd2 nokia800 om-gta01 om-gta02 omap3-pandora
omap3-touchbook omap3evm omap4430-panda omap4430_panda omap5912osk omapzoom
omapzoom2 omapzoom36x openrd-base openrd-client overo palmt650 poodle
powerpc ppc ppc405 ppc603e qemuarm qemumips qemuppc qemux86 sheevaplug
simpad smartq5 spitz tosa ts409 tsx09 usrp-e1xx x86
)
_values -O copts -w -S : architecture ${^tmp}:priority
}
##
# Complete destination name.
_opkg_dest() {
local -a copts=( "$@" )
local -aU tmp
tmp=( ${(f)"$( __opkg_grep_conf '^\s*dest\s+\S+\s+\S+' )"} )
tmp=( ${tmp##[[:space:]]#dest[[:space:]]##} )
tmp=( ${tmp%%[[:space:]]*} )
(( $#tmp )) || {
_message destination
return
}
_values -O copts -w destination $tmp
}
##
# Complete destination-name/path pair.
_opkg_dest_path() {
local -a copts=( "$@" )
local -aU tmp
tmp=( ${(f)"$( __opkg_grep_conf '^\s*dest\s+\S+\s+\S+' )"} )
tmp=( ${tmp##[[:space:]]#dest[[:space:]]##} )
tmp=( ${tmp%%[[:space:]]*} )
(( $#tmp )) || {
_message destination:path
return
}
_values -O copts -w -S : destination ${^tmp}': :_directories'
}
##
# Complete any package name.
_opkg_pkg_all() {
local -a upd copts
zparseopts -a upd -D -E -update
copts=( "$@" )
{ (( ! $#_opkg_cache_pkg_all )) || _cache_invalid opkg-pkg-all } &&
! _retrieve_cache opkg-pkg-all && {
_opkg_cache_pkg_all=( ${(f)"$(
_call_program -p pkg-all ${svc:-opkg} list )"}
)
_opkg_cache_pkg_all=( ${(@)_opkg_cache_pkg_all##[[:space:]]*} )
_opkg_cache_pkg_all=( ${(@)_opkg_cache_pkg_all%%[[:space:]]*} )
_store_cache opkg-pkg-all _opkg_cache_pkg_all
}
(( $#upd )) && return 0
(( $#_opkg_cache_pkg_all )) || {
_message package
return
}
_values -O copts -w package $_opkg_cache_pkg_all
}
##
# Complete installed package name.
_opkg_pkg_inst() {
local -a upd copts
zparseopts -a upd -D -E -update
copts=( "$@" )
{ (( ! $#_opkg_cache_pkg_inst )) || _cache_invalid opkg-pkg-inst } &&
! _retrieve_cache opkg-pkg-inst && {
_opkg_cache_pkg_inst=( ${(f)"$(
_call_program -p pkg-inst ${svc:-opkg} list-installed
)"} )
_opkg_cache_pkg_inst=( ${(@)_opkg_cache_pkg_inst##[[:space:]]*} )
_opkg_cache_pkg_inst=( ${(@)_opkg_cache_pkg_inst%%[[:space:]]*} )
_store_cache opkg-pkg-inst _opkg_cache_pkg_inst
}
(( $#upd )) && return 0
(( $#_opkg_cache_pkg_inst )) || {
_message 'installed package'
return
}
_values -O copts -w 'installed package' $_opkg_cache_pkg_inst
}
##
# Complete new (installable) package name.
_opkg_pkg_new() {
local -a upd copts
zparseopts -a upd -D -E -update
copts=( "$@" )
{ (( ! $#_opkg_cache_pkg_new )) || _cache_invalid opkg-pkg-new } &&
! _retrieve_cache opkg-pkg-new && {
_opkg_pkg_all --update
_opkg_pkg_inst --update
_opkg_cache_pkg_new=( ${_opkg_cache_pkg_all:|_opkg_cache_pkg_inst} )
_store_cache opkg-pkg-new _opkg_cache_pkg_new
}
(( $#upd )) && return 0
(( $#_opkg_cache_pkg_new )) || {
_message 'installable package'
return
}
_values -O copts -w 'installable package' $_opkg_cache_pkg_new
}
##
# Complete upgradeable package name.
_opkg_pkg_upgr() {
local -a upd copts
zparseopts -a upd -D -E -update
copts=( "$@" )
{ (( ! $#_opkg_cache_pkg_upgr )) || _cache_invalid opkg-pkg-upgr } &&
! _retrieve_cache opkg-pkg-upgr && {
_opkg_cache_pkg_upgr=( ${(f)"$(
_call_program -p pkg-upgr ${svc:-opkg} list-upgradable
)"} )
_opkg_cache_pkg_upgr=( ${(@)_opkg_cache_pkg_upgr##[[:space:]]*} )
_opkg_cache_pkg_upgr=( ${(@)_opkg_cache_pkg_upgr%%[[:space:]]*} )
_store_cache opkg-pkg-upgr _opkg_cache_pkg_upgr
}
(( $#upd )) && return 0
(( $#_opkg_cache_pkg_upgr )) || {
_message 'upgradable package'
return
}
_values -O copts -w 'upgradable package' $_opkg_cache_pkg_upgr
}
_opkg() {
local curcontext=$curcontext ret=1 cache_policy help variant svc=$words[1]
local -a line state state_descr args tmp
local -A opt_args val_args
if
zstyle -t ":completion:*:*:$service:*" cache-persists &&
(( ! $+_opkg_cache_pkg_all ))
then
typeset -gaU _opkg_cache_pkg_all
typeset -gaU _opkg_cache_pkg_inst
typeset -gaU _opkg_cache_pkg_new
typeset -gaU _opkg_cache_pkg_upgr
else
local -aU _opkg_cache_pkg_all
local -aU _opkg_cache_pkg_inst
local -aU _opkg_cache_pkg_new
local -aU _opkg_cache_pkg_upgr
fi
zstyle -s ":completion:*:*:$service:*" cache-policy cache_policy
[[ -n $cache_policy ]] ||
zstyle ":completion:*:*:$service:*" cache-policy __opkg_cache_policy
# Options are ordered by long name. Alternative names not listed in the usage
# help are (mostly) ignored
args=(
'*--add-arch=[register architecture with priority]: :_opkg_arch_prio'
'*--add-dest=[register destination with path]: :_opkg_dest_path'
'--autoremove[remove unnecessary packages]'
'--combine[combine upgrade and install operations]'
'(-f --conf)'{-f+,--conf=}'[specify opkg config file]:config file:_files'
'(-d --dest)'{-d+,--dest=}'[specify root directory for package operations]: :_opkg_dest'
'--download-only[make no changes (download only)]'
'--force-checksum[ignore checksum mismatches]'
'--force-downgrade[allow package downgrades]'
'--force-depends[ignore failed dependencies]'
'(--force-maintainer --ignore-maintainer)--force-maintainer[overwrite local config files with upstream changes]'
'--force-overwrite[overwrite files from other packages]'
'--force-postinstall[always run postinstall scripts]'
'--force-reinstall[reinstall packages]'
# This is obnoxiously long; maybe add --force-removal-* to ignored-patterns
'--force-removal-of-dependent-packages[remove packages and all dependencies]'
'--force-remove[ignore failed prerm scripts]'
'--force-space[disable free-space checks]'
'(--force-maintainer --ignore-maintainer)--ignore-maintainer[ignore upstream changes to config files]'
'(-l --lists-dir)'{-l+,--lists-dir=}'[specify package-list directory]:list directory:_directories'
'(--noaction --test)'{--noaction,--test}'[make no changes (test only)]'
'--nodeps[do not follow dependencies]'
# Undocumented variant
'!(-o --offline --offline-root)--offline=:root directory:_directories'
'(-o --offline --offline-root)'{-o+,--offline-root=}'[specify root directory for offline package operations]:root directory:_directories'
'(-A --query-all)'{-A,--query-all}'[query all packages (not just installed)]'
'--recursive[remove packages and all their dependencies]'
'--size[show package sizes]'
'(-t --tmp-dir)'{-t+,--tmp-dir=}'[specify temp directory]:temp directory:_directories'
'(-V --verbosity)'{-V+,--verbosity=}'[specify output verbosity level]: :->verbosity-levels'
'(: -)'{-v,--version}'[display version information]'
'1: :->commands'
'*::: :->extra'
)
# There are a few different variants of opkg, but we'll concern ourselves
# mainly with OpenWRT/Entware vs (up-stream) Yocto
_pick_variant -r variant openwrt=--nocase yocto --help
if [[ $variant == openwrt ]]; then
args+=(
'--cache=[specify cache directory]:cache directory:_directories'
'--nocase[match patterns case-insensitively]'
)
else
args+=(
'*--add-exclude=[register package for exclusion]: :_opkg_pkg_all'
'--cache-dir=[specify cache directory]:cache directory:_directories'
'--host-cache-dir[do not place cache in offline root directory]'
'--no-install-recommends[do not install recommended packages]'
'--prefer-arch-to-version[prefer higher architecture priorities to higher versions]'
'--volatile-cache[use volatile download cache]'
)
fi
_arguments -s -S -C : $args && ret=0
case $state in
commands)
tmp=(
'compare-versions[compare version numbers]'
'configure[configure unpacked package]'
'depends[display dependencies of package]'
'download[download package]'
'files[display files belonging to package]'
'find[search package names and descriptions]'
'flag[flag package]'
'info[display package information]'
'install[install package]'
'list[display available packages]'
'list-changed-conffiles[display user-modified config files]'
'list-installed[display installed packages]'
'list-upgradable[display upgradable packages]'
'print-architecture[display installable architectures]'
'remove[remove package]'
'search[display packages providing file]'
'status[display package status]'
'update[update list of available packages]'
'upgrade[upgrade installed package]'
'whatconflicts[display what conflicts with package]'
'whatdepends[display what depends on package]'
'whatdependsrec[display what depends on package (recursive)]'
'whatprovides[display what provides package]'
'whatrecommends[display what recommends package]'
'whatreplaces[display what replaces package]'
'whatsuggests[display what suggests package]'
)
[[ $variant == openwrt ]] ||
tmp+=( 'clean[clean internal cache]' )
_values sub-command $tmp && ret=0
;;
verbosity-levels)
_values 'verbosity level' \
'0[show errors only]' \
'1[show normal messages (default)]' \
'2[show informational message]' \
'3[show debug messages (level 1)]' \
'4[show debug messages (level 2)]' \
&& ret=0
;;
extra)
case $line[1] in
compare-versions)
case $CURRENT in
1|3) _message 'version string' && ret=0 ;;
2)
_values operator \
'<<[earlier]' \
'<=[earlier or equal]' \
'=[equal]' \
'>=[later or equal]' \
'>>[later]' \
&& ret=0
;;
esac
;;
configure|files|list-*|status)
(( CURRENT == 1 )) && _opkg_pkg_inst && ret=0
;;
depends|what*)
if [[ -n ${opt_args[(I)-A|--query-all]} ]]; then
_opkg_pkg_all && ret=0
else
_opkg_pkg_inst && ret=0
fi
;;
download)
_opkg_pkg_all && ret=0
;;
find|info|list)
(( CURRENT == 1 )) && _opkg_pkg_all && ret=0
;;
flag)
if (( CURRENT == 1 )); then
_values flag hold noprune user ok installed unpacked && ret=0
else
_opkg_pkg_inst && ret=0
fi
;;
install)
_opkg_pkg_new && ret=0
;;
remove)
_opkg_pkg_inst && ret=0
;;
search)
(( CURRENT == 1 )) && _files && ret=0
;;
upgrade)
_opkg_pkg_upgr && ret=0
;;
esac
;;
esac
(( ret && $#state )) && _message 'no more arguments' && ret=0
return ret
}
_opkg "$@"
|