This file is indexed.

/usr/share/pyshared/doit/cmd_completion.py is in python-doit 0.24.0-1.

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
320
321
322
323
324
325
326
327
328
329
330
331
"""generate shell script with tab complention code for doit commands/tasks"""

import sys
from string import Template

from .exceptions import InvalidCommand
from .cmd_base import DoitCmdBase


opt_shell = {
    'name': 'shell',
    'short': 's',
    'long': 'shell',
    'type': str,
    'default': 'bash',
    'help': 'Completion code for SHELL. default: "bash". options: [bash, zsh]',
    }

opt_hardcode_tasks = {
    'name': 'hardcode_tasks',
    'short': '',
    'long': 'hardcode-tasks',
    'type': bool,
    'default': False,
    'help': 'Hardcode tasks from current task list.',
    }



class TabCompletion(DoitCmdBase):
    """generate scripts for tab-completion

    If hardcode-tasks options is chosen it will get the task
    list from the current dodo file and include in the completion script.
    Otherwise the script will dynamically call `doit list` to get the list
    of tasks.

    If it is completing a sub-task (contains ':' in the name),
    it will always call doit while evaluating the options.

    """
    doc_purpose = "generate script for tab-complention"
    doc_usage = ""
    doc_description = None

    cmd_options = (opt_shell, opt_hardcode_tasks, )

    def execute(self, opt_values, pos_args):
        if opt_values['shell'] == 'bash':
            self._generate_bash(opt_values, pos_args)
        elif opt_values['shell'] == 'zsh':
            self._generate_zsh(opt_values, pos_args)
        else:
            msg = 'Invalid option for --shell "{0}"'
            raise InvalidCommand(msg.format(opt_values['shell']))


    def _generate_bash(self, opt_values, pos_args):
        # some applications built with doit do not use dodo.py files
        for opt in self.options:
            if opt.name=='dodoFile':
                get_dodo_part = bash_get_dodo
                pt_list_param = '--file="$dodof"'
                break
        else:
            get_dodo_part = ''
            pt_list_param = ''

        # dict with template values
        pt_bin_name = sys.argv[0].split('/')[-1]
        tmpl_vars = {
            'pt_bin_name': pt_bin_name,
            'pt_cmds': ' '.join(self.doit_app.sub_cmds),
            'pt_list_param': pt_list_param,
            }

        # if hardcode tasks
        if opt_values['hardcode_tasks']:
            self.task_list, self.config = self._loader.load_tasks(
                self, opt_values, pos_args)
            tmpl_vars['pt_tasks'] = '"{0}"'.format(
                ' '.join(t.name for t in self.task_list if not t.is_subtask))
        else:
            tmpl_list_cmd = "$({0} list {1} --quiet 2>/dev/null)"
            tmpl_vars['pt_tasks'] = tmpl_list_cmd.format(pt_bin_name,
                                                         pt_list_param)

        template = Template(bash_start + bash_opt_file + get_dodo_part +
                            bash_task_list + bash_end)
        self.outstream.write(template.safe_substitute(tmpl_vars))


    @staticmethod
    def _zsh_arg_line(opt):
        """create a text line for completion of a command arg"""
        # '(-c|--continue)'{-c,--continue}'[continue executing tasks...]' \
        # '--db-file[file used to save successful runs]' \
        if opt.short and opt.long:
            tmpl = ("'(-{0.short}|--{0.long})'{{-{0.short},--{0.long}}}'"
                    "[{help}]' \\")
        elif not opt.short and opt.long:
            tmpl = "'--{0.long}[{help}]' \\"
        elif opt.short and not opt.long:
            tmpl = "'-{0.short}[{help}]' \\"
        else: # without short or long options cant be really used
            return ''
        ohelp = opt.help.replace(']', '\]')
        return tmpl.format(opt, help=ohelp).replace('\n', ' ')


    @classmethod
    def _zsh_arg_list(cls, cmd):
        """return list of arguments lines for zsh completion"""
        args = []
        for opt in cmd.options:
            args.append(cls._zsh_arg_line(opt))
        if 'TASK' in cmd.doc_usage:
            args.append("'*::task:(($tasks))'")
        if 'COMMAND' in cmd.doc_usage:
            args.append("'::cmd:(($commands))'")
        return args

    @classmethod
    def _zsh_cmd_args(cls, cmd):
        """create the content for "case" statement with all command options """
        arg_lines = cls._zsh_arg_list(cmd)
        tmpl = """
      ({cmd_name})
          _command_args=(
            {args_body}
            ''
        )
      ;;
"""
        args_body = '\n            '.join(arg_lines)
        return tmpl.format(cmd_name=cmd.name, args_body=args_body)


    # TODO:
    # detect correct dodo-file location
    # complete sub-tasks
    # task options
    def _generate_zsh(self, opt_values, pos_args):
        # deal with doit commands
        cmds_desc = []
        cmds_args = []
        for cmd in self.doit_app.sub_cmds.values():
            cmds_desc.append("    '{0}: {1}'".format(cmd.name, cmd.doc_purpose))
            cmds_args.append(self._zsh_cmd_args(cmd))

        template_vars = {
            'pt_bin_name': sys.argv[0].split('/')[-1],
            'pt_cmds':'\n    '.join(cmds_desc),
            'pt_cmds_args':'\n'.join(cmds_args),
        }

        if opt_values['hardcode_tasks']:
            self.task_list, self.config = self._loader.load_tasks(
                self, opt_values, pos_args)
            lines = []
            for task in self.task_list:
                if not task.is_subtask:
                    lines.append("'{0}: {1}'".format(task.name, task.doc))
            template_vars['pt_tasks'] = '(\n{0}\n)'.format('\n'.join(lines))
        else:
            tmpl_tasks = Template('''("${(f)$($pt_bin_name list --template '{name}: {doc}')}")''')
            template_vars['pt_tasks'] = tmpl_tasks.safe_substitute(template_vars)


        template = Template(zsh_start)
        self.outstream.write(template.safe_substitute(template_vars))




############## templates
# Variables starting with 'pt_' belongs to the Python Template
# to generate the script.
# Remaining are shell variables used in the script.


################################################################
############### bash template


bash_start = """# bash completion for $pt_bin_name
# auto-generate by `$pt_bin_name tabcomplention`

# to activate it you need to 'source' the generate script
# $ source <generated-script>

# reference => http://www.debian-administration.org/articles/317
# patch => http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=711879

_$pt_bin_name()
{
    local cur prev words cword basetask sub_cmds tasks i dodof
    COMPREPLY=() # contains list of words with suitable completion
    # remove colon from word separator list because doit uses colon on task names
    _get_comp_words_by_ref -n : cur prev words cword
    # list of sub-commands
    sub_cmds="$pt_cmds"

"""

# FIXME - wont be necessary after adding support for options with type
bash_opt_file = """
    # options that take file/dir as values should complete file-system
    if [[ "$prev" == "-f" || "$prev" == "-d" || "$prev" == "-o" ]]; then
        _filedir
        return 0
    fi
    if [[ "$cur" == *=* ]]; then
        prev=${cur/=*/}
        cur=${cur/*=/}
        if [[ "$prev" == "--file=" || "$prev" == "--dir=" || "$prev" == "--output-file=" ]]; then
            _filedir -o nospace
            return 0
        fi
    fi

"""


bash_get_dodo = """
    # get name of the dodo file
    for (( i=0; i < ${#words[@]}; i++)); do
        case "${words[i]}" in
        -f)
            dodof=${words[i+1]}
            break
            ;;
        --file=*)
            dodof=${words[i]/*=/}
            break
            ;;
        esac
    done
    # dodo file not specified, use default
    if [ ! $dodof ]
      then
         dodof="dodo.py"
    fi

"""

bash_task_list = """
    # get task list
    # if it there is colon it is getting a subtask, complete only subtask names
    if [[ "$cur" == *:* ]]; then
        # extract base task name (remove everything after colon)
        basetask=${cur%:*}
        # sub-tasks
        tasks=$($pt_bin_name list $pt_list_param --quiet --all ${basetask} 2>/dev/null)
        COMPREPLY=( $(compgen -W "${tasks}" -- ${cur}) )
        __ltrim_colon_completions "$cur"
        return 0
    # without colons get only top tasks
    else
        tasks=$pt_tasks
    fi

"""

bash_end = """
    # match for first parameter must be sub-command or task
    # FIXME doit accepts options "-" in the first parameter but we ignore this case
    if [[ ${cword} == 1 ]] ; then
        COMPREPLY=( $(compgen -W "${sub_cmds} ${tasks}" -- ${cur}) )
        return 0
    fi

    # if command is help complete with tasks or sub-commands
    if [[ ${words[1]} == "help" ]] ; then
        COMPREPLY=( $(compgen -W "${sub_cmds} ${tasks}" -- ${cur}) )
        return 0
    fi

    # if there is already one parameter match only tasks (no commands)
    COMPREPLY=( $(compgen -W "${tasks}" -- ${cur}) )

}
complete -F _$pt_bin_name $pt_bin_name
"""



################################################################
############### zsh template


zsh_start = """#compdef $pt_bin_name

_$pt_bin_name() {
    local -a commands tasks
    # format is 'completion:description'
    commands=(
    $pt_cmds
    )

    # split output by lines to create an array
    tasks=$pt_tasks

    # complete command or task name
    if (( CURRENT == 2 )); then
        _arguments -A : '::cmd:(($commands))' '::task:(($tasks))'
        return
    fi

    # revome program name from $words and decrement CURRENT
    local curcontext context state state_desc line
    _arguments -C '*:: :->'

    # complete sub-command or task options
    local -a _command_args
    case "$words[1]" in
        $pt_cmds_args

        # default completes task names
        (*)
           _command_args='*::task:(($tasks))'
        ;;
    esac

    # -A no options will be completed after the first non-option argument
    _arguments -A : $_command_args
    return 0
}

_doit
"""