From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 5650 invoked by alias); 7 Jun 2018 19:53:03 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: List-Unsubscribe: X-Seq: 42948 Received: (qmail 17113 invoked by uid 1010); 7 Jun 2018 19:53:03 -0000 X-Qmail-Scanner-Diagnostics: from mail-it0-f47.google.com by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.99.2/21882. spamassassin: 3.4.1. Clear:RC:0(209.85.214.47):SA:0(-1.9/5.0):. Processed in 2.246584 secs); 07 Jun 2018 19:53:03 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_PASS,T_DKIMWL_WL_MED,T_DKIM_INVALID autolearn=ham autolearn_force=no version=3.4.1 X-Envelope-From: dana@dana.is X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dana-is.20150623.gappssmtp.com; s=20150623; h=from:content-transfer-encoding:mime-version:subject:message-id:date :to; bh=WR41zLySAjS5FsDazabUAeG4OsXjCQAdSe4NnNrjlUw=; b=CqaosmhaXODpYFtWVxAz/YW8WZ6eqyXA+9VpZapFacKoqDcXlRk/ZeVl17PWkvdW0e uXV8kM6ytC/tIiHpPRwxvmu4r9YvWzYMhQUTbo4Bd61wa3hsdtUEUTtl+slDOevBiV9J RBOCJcAnftRZl1JqPQ+TPmNkgclQZ/glfpqYlFEVOTmZpNBHfirAC9IPtpL0EpeUY8Ek o+Qfy8N5Bot4ZBFwfbqztiuv4xdF1GIKVoKdZcX2oMKDfs5ij9dC/GUXb2dbHVLsQnD/ ghduFf8wwzfIUQHBm2MUa5btMZKSRXMUhsPSzFRPh6DD8+QfhVfSlCKkwS6kUoeWb/r8 oa3g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:content-transfer-encoding:mime-version :subject:message-id:date:to; bh=WR41zLySAjS5FsDazabUAeG4OsXjCQAdSe4NnNrjlUw=; b=IkPRhQXPILUgUe4ri6TFyhOfN59Ad4rkStOCf3ksm/qrGp5Bx1U8VnzoxeynyLQ2tf EeBTELuRcrV9rLeiRFk3Bpycsrwpv8SAqV1oA4NZuu0e/qIPF3v742mNcenCgSh54utf EXOIm85BxUFNyzgWMjKIr1apv/t3KpOyWh/l67qNyRAF2z/DGlSKBsNLCRDIR8HJrIM2 CSnCVaGc8NXWWXg2IasLsKnmzsGHbYjKi3JbqQw2+7atJ5WVnCNWApIyucebiZ02ccE1 W0dT4dL8HfrZcreNUbsITKKirmCHOd5EGeAhSzb0xEMoniX/i5dYDI8r9jSh4LJtxuV0 I9cw== X-Gm-Message-State: APt69E124W00yBiWH2jSnxJjqk2zdDMBDPo+khub5Z9nkjLWL72opho1 5UI0XXGntIXKsWX7kIi68WfnQcy9oZ4= X-Google-Smtp-Source: ADUXVKITzpG7QMLRJEKSYe+5rbxtcRc0CRiXhkR2otnZTmgge/iw41tR85g1Kr7yBizHUNDsqrFKCw== X-Received: by 2002:a24:dd3:: with SMTP id 202-v6mr3201668itx.77.1528401178072; Thu, 07 Jun 2018 12:52:58 -0700 (PDT) From: dana Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Mime-Version: 1.0 (Mac OS X Mail 10.3 \(3273\)) Subject: [PATCH] Completion: Add _opkg Message-Id: <58B81730-E877-493F-AA09-87B0653D9FFF@dana.is> Date: Thu, 7 Jun 2018 14:52:56 -0500 To: Zsh workers X-Mailer: Apple Mail (2.3273) Hey again, Here's my first semi-complicated function. Fortunately it's a new = addition, so not too risky. opkg is an APT-style package manager which is used mostly by embedded = Linux distributions such as OpenWrt. It's a fork of an earlier project called = ipkg. I think i'm pretty happy with how this functions, but i do have concerns = about some of the methods i used. Please let me know if you think there's a = more accurate/efficient/idiomatic way i can do something. One thing i did differently from most of the other completers i've seen = is add an option to use caching but not actually keep the cache array(s) in = memory (i.e., don't declare them global). I noticed with APT that these arrays = can grow to several MiB in size, and although the list of packages in the opkg distribution i use is much smaller, it still bothered me to keep that = sitting around on such a resource-constrained device. Might be over-kill though, = idk dana diff --git a/Completion/Linux/Command/_opkg = b/Completion/Linux/Command/_opkg new file mode 100644 index 000000000..60b987e4a --- /dev/null +++ b/Completion/Linux/Command/_opkg @@ -0,0 +1,467 @@ +#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 +# Set to yes to enable caching of package names. Usually disabled by = default. +# +# % zstyle ':completion:*:opkg:*' cache-path +# Set to a directory path to override the default cache-file = directory. +# +# % zstyle ':completion:*:opkg:*' cache-persists +# 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 ... +# 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 ... +# Set to one or more paths or glob patterns to override the defaults = used when +# searching opkg configuration data. + +## +# Check cache validity. +__opkg_cache_policy() { + local -a tmp + + # Always invalidate if it's been over a week + tmp=3D( $1(#qmw+1N) ) + (( $#tmp )) && return 0 + + zstyle -a ":completion:$curcontext:" status-paths tmp + + if (( $#tmp )); then + tmp=3D( $~tmp(#qN) ) + else + tmp=3D( + {/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=3D( $~tmp(#qN) ) + else + tmp=3D( + {,/opt}/etc/{i,o}pkg*.conf(#q-.N) + {,/opt}/etc/{i,o}pkg/*.conf(#q-.N) + ) + fi + + (( $#tmp )) || return 1 + + GREP_OPTIONS=3D command grep -sE "$@" $tmp +} + +## +# Complete archicture/priority pair. +# +# Architecture names are essentially arbitrary (up to the packager), so = we can't +# really complete every possibility here =E2=80=94 but we'll account = for most of the +# popular ones. +_opkg_arch_prio() { + local -a copts=3D( "$@" ) + local -aU tmp + + [[ -prefix '*:*' ]] && { + _message priority + return + } + + # Already configured arches + tmp=3D( ${(f)"$( _call_program architectures $svc print-architecture = )"} ) + tmp=3D( ${${tmp##arch[ ]##}%% *} ) + + tmp+=3D( + # '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 =C3=85ngstr=C3=B6m 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=3D( "$@" ) + local -aU tmp + + tmp=3D( ${(f)"$( __opkg_grep_conf '^\s*dest\s+\S+\s+\S+' )"} ) + tmp=3D( ${tmp##[[:space:]]#dest[[:space:]]##} ) + tmp=3D( ${tmp%%[[:space:]]*} ) + + (( $#tmp )) || { + _message destination + return + } + _values -O copts -w destination $tmp +} + +## +# Complete destination-name/path pair. +_opkg_dest_path() { + local -a copts=3D( "$@" ) + local -aU tmp + + [[ -prefix '*:*' ]] && { + _directories "$@" + return + } + + tmp=3D( ${(f)"$( __opkg_grep_conf '^\s*dest\s+\S+\s+\S+' )"} ) + tmp=3D( ${tmp##[[:space:]]#dest[[:space:]]##} ) + tmp=3D( ${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=3D( "$@" ) + + { (( ! $#_opkg_cache_pkg_all )) || _cache_invalid opkg-pkg-all } && + ! _retrieve_cache opkg-pkg-all && { + _opkg_cache_pkg_all=3D( ${(f)"$( _call_program pkg-all ${svc:-opkg} = list )"} ) + _opkg_cache_pkg_all=3D( ${(@)_opkg_cache_pkg_all##[[:space:]]*} ) + _opkg_cache_pkg_all=3D( ${(@)_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=3D( "$@" ) + + { (( ! $#_opkg_cache_pkg_inst )) || _cache_invalid opkg-pkg-inst } && + ! _retrieve_cache opkg-pkg-inst && { + _opkg_cache_pkg_inst=3D( ${(f)"$( + _call_program pkg-inst ${svc:-opkg} list-installed + )"} ) + _opkg_cache_pkg_inst=3D( ${(@)_opkg_cache_pkg_inst##[[:space:]]*} ) + _opkg_cache_pkg_inst=3D( ${(@)_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=3D( "$@" ) + + { (( ! $#_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=3D( = ${_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=3D( "$@" ) + + { (( ! $#_opkg_cache_pkg_upgr )) || _cache_invalid opkg-pkg-upgr } && + ! _retrieve_cache opkg-pkg-upgr && { + _opkg_cache_pkg_upgr=3D( ${(f)"$( + _call_program pkg-upgr ${svc:-opkg} list-upgradable + )"} ) + _opkg_cache_pkg_upgr=3D( ${(@)_opkg_cache_pkg_upgr##[[:space:]]*} ) + _opkg_cache_pkg_upgr=3D( ${(@)_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=3D$curcontext ret=3D1 cache_policy help variant = svc=3D$words[1] + local -a context 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=3D( + '*--add-arch=3D[register architecture with priority]: = :_opkg_arch_prio' + '*--add-dest=3D[register destination with path]: :_opkg_dest_path' + '--autoremove[remove unnecessary packages]' + '--combine[combine upgrade and install operations]' + '(-f --conf)'{-f+,--conf=3D}'[specify opkg config file]:config = file:_files' + '(-d --dest)'{-d+,--dest=3D}'[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=3D}'[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=3D:root = directory:_directories' + '(-o --offline --offline-root)'{-o+,--offline-root=3D}'[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=3D}'[specify temp directory]:temp = directory:_directories' + '(-V --verbosity)'{-V+,--verbosity=3D}'[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=3D--nocase yocto --help + + if [[ $variant =3D=3D openwrt ]]; then + args+=3D( + '--cache=3D[specify cache directory]:cache = directory:_directories' + '--nocase[match patterns case-insensitively]' + ) + else + args+=3D( + '*--add-exclude=3D[register package for exclusion]: = :_opkg_pkg_all' + '--cache-dir=3D[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=3D0 + + case $state in + commands) + tmp=3D( + '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 =3D=3D openwrt ]] || + tmp+=3D( 'clean[clean internal cache]' ) + + _values sub-command $tmp && ret=3D0 + ;; + 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=3D0 + ;; + extra) + case $line[1] in + compare-versions) + case $CURRENT in + 1|3) _message 'version string' && ret=3D0 ;; + 2) + _values operator \ + '<<[earlier]' \ + '<=3D[earlier or equal]' \ + '=3D[equal]' \ + '>=3D[later or equal]' \ + '>>[later]' \ + && ret=3D0 + ;; + esac + ;; + configure|files|list-*|status) + (( CURRENT =3D=3D 1 )) && _opkg_pkg_inst && ret=3D0 + ;; + depends|what*) + if [[ -n ${opt_args[(I)-A|--query-all]} ]]; then + _opkg_pkg_all && ret=3D0 + else + _opkg_pkg_inst && ret=3D0 + fi + ;; + download) + _opkg_pkg_all && ret=3D0 + ;; + find|info|list) + (( CURRENT =3D=3D 1 )) && _opkg_pkg_all && ret=3D0 + ;; + flag) + if (( CURRENT =3D=3D 1 )); then + _values flag hold noprune user ok installed unpacked && = ret=3D0 + else + _opkg_pkg_inst && ret=3D0 + fi + ;; + install) + _opkg_pkg_new && ret=3D0 + ;; + remove) + _opkg_pkg_inst && ret=3D0 + ;; + search) + (( CURRENT =3D=3D 1 )) && _files && ret=3D0 + ;; + upgrade) + _opkg_pkg_upgr && ret=3D0 + ;; + esac + ;; + esac + + (( ret && $#state )) && _message 'no more arguments' && ret=3D0 + return ret +} + +_opkg "$@"