This file is indexed.

/usr/share/bash-completion/completions/bts is in devscripts 2.17.6+deb9u2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
# /usr/share/bash-completion/completions/bts
# Bash command completion for ‘bts(1)’.
# Documentation: ‘bash(1)’, section “Programmable Completion”.

# Copyright © 2015, Nicholas Bamber <nicholas@periapt.co.uk>

_get_version_from_package()
{
    local _pkg=$1
    [[ -n $_pkg ]] || return 
    apt-cache madison $_pkg 2> /dev/null | cut -d'|' -f2 | sort | uniq | paste -s -d' '
}

# This works really well unless someone sets up nasty firewall rules like:
# sudo iptables -A OUTPUT -d 206.12.19.140 -j DROP
# sudo iptables -A OUTPUT -d 140.211.166.26 -j DROP
# These block access to the Debian bugs SOAP interface.
# Hence we need a timeout.
# Of course if the SOAP interface is blocked then so is the caching interface.
# So really this would only affect someone who only accidentally hit the TAB key.
_get_version_from_bug()
{
    local -i _bug=$1
    _get_version_from_package $( bts --soap-timeout=2 status $_bug fields:package 2> /dev/null | cut -f2 )
}

_suggest_packages()
{
    apt-cache --no-generate pkgnames "$1" 2> /dev/null
}

_suggest_bugs()
{
    bts --offline listcachedbugs "$1" 2> /dev/null
}

_bts()
{
    local cur prev words cword
    _init_completion -n = || return

    # Note:
    # The long lists of subcommands are not the same and not necessarily to be kept in sync.
    # The first is used to suggest commands after a '.' or ','.
    # The second is to hook in special handling (which may be as little as admitting we 
    # we can't handle it further) or the default special handling (list of bug ids).
    # This also includes "by" and "with" which are not even subcommands.
    # The third is similar to the first - what to suggest after the bts command (and options).
    # but this includes the "help" and "version" commands.

    # A sequence of bts commands can be on one command line separated by "." or ",". 
    if [[ $prev == @(.|,) ]]; then
        COMPREPLY=( $( compgen -W 'show bugs unmerge select status clone done reopen archive unarchive retitle summary submitter reassign found notfound fixed notfixed block unblock merge forcemerge tags affects user usertags claim unclaim severity forwarded notforwarded package limit owner noowner subscribe unsubscribe reportspam spamreport' -- "$cur" ) )
        return 0
    fi

    # Identify the last command in the command line.
    local special punctuation i
    for (( i=${#words[@]}-1; i > 0; i-- )); do
        if [[ ${words[i]} == @(show|bugs|select|limit|unmerge|status|clone|done|reopen|archive|unarchive|retitle|summary|submitter|reassign|found|notfound|fixed|notfixed|block|unblock|merge|forcemerge|tags|affects|user|usertags|claim|unclaim|severity|forwarded|notforwarded|package|owner|noowner|subscribe|unsubscribe|reportspam|spamreport|cache|cleancache|by|with) ]]; then
            special=${words[i]}
            break
        fi
        if [[ ${words[i]} == @(+|-|=) ]]; then
            punctuation=${words[i]}
        fi
    done

    if [[ -n $special ]]; then

    # The command separator must be surrounded by white space.
    if [[ "$cur" == @(,|.) ]]; then
        COMPREPLY=( $cur )
        return 0
    fi

    case $special in
        show|bugs)
            # bugs/show supports a few limited options
            # but as args we accept bug ids, package names and release-critical
            if [[ "$cur" == -* ]]; then
                COMPREPLY=( $( compgen -W '-o --offline --online -m --mbox \
                    --no-cache --cache' -- "$cur" ) )
            elif [[ "$cur" == release-critical/* ]]; then
                local _pkg=${cur#release-critical/}
                COMPREPLY=( $( _suggest_packages "$_pkg" | sed -e's!^!release-critical/!' ) )
            else
                COMPREPLY=( $( compgen -W 'release-critical RC' -- "$cur" ) \
                    $( _suggest_bugs "$cur" ) \
                    $( _suggest_packages "$cur" ) )
            fi
            return 0
            ;;
        status)
            # we accept "verbose" and bug ids
            COMPREPLY=( $( compgen -W 'verbose' -- "$cur" ) \
                $( _suggest_bugs "$cur" ) )
            return 0
            ;;
        clone)
            # we accept 1 bug id and then generate new clone ids
            if [[ "$prev" == +([0-9]) ]]; then
                COMPREPLY=( $( compgen -W '-1' -- "$cur" ) )
            elif [[ "$prev" == -+([0-9]) ]]; then
                local -i j
                (( j=$prev-1 ))
                COMPREPLY=( $( compgen -W $j -- "$cur" ) )
            else
                COMPREPLY=( $( _suggest_bugs "$cur" ) )
            fi
            return 0
            ;;
        done|found|notfound|fixed|notfixed)
            # Try to guess the version 
            if [[ "$prev" == +([0-9]) ]]; then
                local _versions=$( _get_version_from_bug $prev )
                if [[ -n $_versions ]]; then
                    COMPREPLY=( $( compgen -W $_versions -- "$cur" ) )
                else
                    COMPREPLY=( )
                fi
            else
                COMPREPLY=( $( _suggest_bugs "$cur" ) )
            fi
            return 0
            ;;
        reopen|claim|unclaim|owner|subscribe|unsubscribe)
            if [[ "$prev" == +([0-9]) && -n $DEBEMAIL ]]; then
                COMPREPLY=( $( compgen -W $DEBEMAIL -- "$cur" ) )
            else
                COMPREPLY=( $( _suggest_bugs "$cur" ) )
            fi
            return 0
            ;;
        reassign)
            # Must have at least one bug id.
            # Once we have a package name, all that remains is an optional version.
            if [[ "$prev" == $special ]]; then
                COMPREPLY=( $( _suggest_bugs "$cur" ) )
            elif [[ "$prev" == +([0-9]) ]]; then
                COMPREPLY=( $( _suggest_bugs "$cur" ) \
                $( _suggest_packages "$cur" ) )
            else
                local _versions=$( _get_version_from_package $prev )
                COMPREPLY=( $( compgen -W $_versions -- "$cur" ) )
            fi
            return 0
            ;;
        block|unblock)
            # Must have at least one bug id.
            if [[ "$prev" == $special ]]; then
                COMPREPLY=( $( _suggest_bugs "$cur" ) )
            elif [[ "$prev" == +([0-9]) ]]; then
                COMPREPLY=( $( compgen -W 'by with' -- "$cur" ) )
            else
                COMPREPLY=( )
            fi
            return 0
            ;;
        unmerge|forwarded|notforwarded|noowner)
            # Must have at most one bug id.
            if [[ "$prev" == $special ]]; then
                COMPREPLY=( $( _suggest_bugs "$cur" ) )
            else
                COMPREPLY=( )
            fi
            return 0
            ;;
        tags)
            # Must have one bug id.
            if [[ "$prev" == $special ]]; then
                COMPREPLY=( $( _suggest_bugs "$cur" ) )
            elif [[ -n $punctuation ]]; then
                # The official list is mirrored
                # https://www.debian.org/Bugs/server-control#tag
                # in the variable @gTags; we copy it verbatim here.
                COMPREPLY=( $( compgen -W 'patch wontfix moreinfo unreproducible fixed potato woody sid help security upstream pending sarge sarge-ignore experimental d-i confirmed ipv6 lfs fixed-in-experimental fixed-upstream l10n newcomer a11y ftbfs etch etch-ignore lenny lenny-ignore squeeze squeeze-ignore wheezy wheezy-ignore jessie jessie-ignore stretch stretch-ignore buster buster-ignore bullseye bullseye-ignore' -- "$cur" ) )
            else
                COMPREPLY=()
                COMPREPLY[0]='= '
                COMPREPLY[1]='+ '
                COMPREPLY[2]='- '
            fi
            return 0
            ;;
        affects)
            # Must have one bug id.
            if [[ "$prev" == $special ]]; then
                COMPREPLY=( $( _suggest_bugs "$cur" ) )
            elif [[ -n $punctuation ]]; then
                COMPREPLY=( $( _suggest_packages "$cur" ) )
            else
                COMPREPLY=()
                COMPREPLY[0]='= '
                COMPREPLY[1]='+ '
                COMPREPLY[2]='- '
            fi
            return 0
            ;;
        user)
            if [[ "$prev" == $special && -n $DEBEMAIL ]]; then
                COMPREPLY=( $( compgen -W $DEBEMAIL -- "$cur" ) )
            else
                COMPREPLY=( )
            fi
            return 0
            ;;
        usertags)
            # Must have one bug id.
            if [[ "$prev" == $special ]]; then
                COMPREPLY=( $( _suggest_bugs "$cur" ) )
            elif [[ -z $punctuation ]]; then
                COMPREPLY=()
                COMPREPLY[0]='= '
                COMPREPLY[1]='+ '
                COMPREPLY[2]='- '
            else 
                COMPREPLY=()
            fi
            return 0
            ;;
        severity)
            if [[ "$prev" == $special ]]; then
                COMPREPLY=( $( _suggest_bugs "$cur" ) )
            elif [[ "$prev" == +([0-9]) ]]; then
                COMPREPLY=( $( compgen -W 'wishlist minor normal important serious \
                    grave critical' -- "$cur" ) )
            else
                COMPREPLY=()
            fi
            return 0
            ;;
        select|limit)
            # can't handle ":". Give up for now.
            COMPREPLY=( )
            return 0
            ;;
        package)
            COMPREPLY=( $( _suggest_packages "$cur" ) )
            return 0
            ;;
        cache)
            # cache supports a few limited options
            # but as args we accept bug ids, package names and release-critical
            if [[ "$prev" == --cache-mode ]]; then
                COMPREPLY=( $( compgen -W 'min mbox full' -- "$cur" ) )
            elif [[ "$cur" == release-critical/* ]]; then
                local _pkg=${cur#release-critical/}
                COMPREPLY=( $( _suggest_packages "$_pkg" | sed -e's!^!release-critical/!' ) )
            elif [[ "$cur" == -* ]]; then
                COMPREPLY=( $( compgen -W '--cache-mode --force-refresh -f \
                    --include-resolved -q --quiet' -- "$cur" ) )
            else
                COMPREPLY=( $( compgen -W 'release-critical RC' -- "$cur" ) \
                    $( _suggest_packages "$cur" ) )
            fi
            return 0
            ;;
        cleancache)
            if [[ "$prev" == $special ]]; then
                COMPREPLY=( $( compgen -W 'ALL' -- "$cur" ) \
                    $( _suggest_bugs "$cur" ) \
                    $( _suggest_packages "$cur" ) )
            else
                COMPREPLY=( )
            fi
            return 0
            ;;
        *)
            COMPREPLY=( $( _suggest_bugs "$cur" ) )
            return 0
            ;;
        esac
    fi
        
    case $prev in
        --cache-mode)
             COMPREPLY=( $( compgen -W 'min mbox full' -- "$cur" ) )
             return 0
             ;;
        --cache-delay)
             COMPREPLY=( $( compgen -W '5 60 120 240 600' -- "$cur" ) )
             return 0
             ;;
    esac

    if [[ "$cur" == -* ]]; then
        COMPREPLY=( $( compgen -W '-o --offline --online -n --no-action --cache --no-cache --cache-mode --cache-delay --mbox --no-use-default-cc --mutt --no-mutt -f --force-refresh --no-force-refresh --only-new --include-resolved --no-include-resolved --no-ack --ack -i --interactive --force-interactivei --no-interactive -q --quiet' -- "$cur" ) )
    else
        COMPREPLY=( $( compgen -W 'show bugs unmerge select status clone done reopen archive unarchive retitle summary submitter reassign found notfound fixed notfixed block unblock merge forcemerge tags affects user usertags claim unclaim severity forwarded notforwarded package limit owner noowner subscribe unsubscribe reportspam spamreport cache cleancache version help' -- "$cur" ) )
    fi

    # !!! not handled !!!
    # --mailreader=READER
    # --cc-addr=CC_EMAIL_ADDRESS
    # --use-default-cc
    # --sendmail=SENDMAILCMD
    # --smtp-host=SMTPHOST
    # --smtp-username=USERNAME
    # --smtp-password=PASSWORD
    # --smtp-helo=HELO
    # --bts-server
    # --no-conf, --noconf
    #
    # anything with colons for now
    # for similar reasons having issues with tags XXXX = 
    # no special handling for select

    return 0
} &&
complete -F _bts bts


# Local variables:
# coding: utf-8
# mode: shell-script
# indent-tabs-mode: nil
# End:
# vim: fileencoding=utf-8 filetype=sh expandtab shiftwidth=4 :