1 #!/usr/bin/env bash
2
3 #### -A function prints functions
4 add () { expr 4 + 4; }
5 div () { expr 6 / 2; }
6 ek () { echo hello; }
7 __ec () { echo hi; }
8 _ab () { expr 10 % 3; }
9 compgen -A function
10 echo --
11 compgen -A function _
12 ## status: 0
13 ## STDOUT:
14 __ec
15 _ab
16 add
17 div
18 ek
19 --
20 __ec
21 _ab
22 ## END
23
24 #### Invalid syntax
25 compgen -A foo
26 echo status=$?
27 ## stdout: status=2
28
29 #### how compgen calls completion functions
30 foo_complete() {
31 # first, cur, prev
32 argv.py argv "$@"
33 argv.py COMP_WORDS "${COMP_WORDS[@]}"
34 argv.py COMP_CWORD "${COMP_CWORD}"
35 argv.py COMP_LINE "${COMP_LINE}"
36 argv.py COMP_POINT "${COMP_POINT}"
37 #return 124
38 COMPREPLY=(one two three)
39 }
40 compgen -F foo_complete foo a b c
41 ## STDOUT:
42 ['argv', 'compgen', 'foo', '']
43 ['COMP_WORDS']
44 ['COMP_CWORD', '-1']
45 ['COMP_LINE', '']
46 ['COMP_POINT', '0']
47 one
48 two
49 three
50 ## END
51
52 #### complete -o -F (git)
53 foo() { echo foo; }
54 wrapper=foo
55 complete -o default -o nospace -F $wrapper git
56 ## status: 0
57
58 #### compopt with invalid syntax
59 compopt -o invalid
60 echo status=$?
61 ## stdout: status=2
62
63 #### compopt fails when not in completion function
64 # NOTE: Have to be executing a completion function
65 compopt -o filenames +o nospace
66 ## status: 1
67
68 #### compgen -f on invalid dir
69 compgen -f /non-existing-dir/
70 ## status: 1
71 ## stdout-json: ""
72
73 #### compgen -f
74 mkdir -p $TMP/compgen
75 touch $TMP/compgen/{one,two,three}
76 cd $TMP/compgen
77 compgen -f | sort
78 echo --
79 compgen -f t | sort
80 ## STDOUT:
81 one
82 three
83 two
84 --
85 three
86 two
87 ## END
88
89 #### compgen -v with local vars
90 v1_global=0
91 f() {
92 local v2_local=0
93 compgen -v v
94 }
95 f
96 ## STDOUT:
97 v1_global
98 v2_local
99 ## END
100
101 #### compgen -v on unknown var
102 compgen -v __nonexistent__
103 ## status: 1
104 ## stdout-json: ""
105
106 #### compgen -v P
107 cd > /dev/null # for some reason in bash, this makes PIPESTATUS appear!
108 compgen -v P | grep -E '^PATH|PWD' | sort
109 ## STDOUT:
110 PATH
111 PWD
112 ## END
113
114 #### compgen with actions: function / variable / file
115 mkdir -p $TMP/compgen2
116 touch $TMP/compgen2/{PA,Q}_FILE
117 cd $TMP/compgen2 # depends on previous test above!
118 PA_FUNC() { echo P; }
119 Q_FUNC() { echo Q; }
120 compgen -A function -A variable -A file PA
121 ## STDOUT:
122 PA_FUNC
123 PATH
124 PA_FILE
125 ## END
126
127 #### compgen with actions: alias, setopt
128 alias v_alias='ls'
129 alias v_alias2='ls'
130 alias a1='ls'
131 compgen -A alias -A setopt v
132 ## STDOUT:
133 v_alias
134 v_alias2
135 verbose
136 vi
137 ## END
138
139 #### compgen with actions: shopt
140 compgen -A shopt -P [ -S ] nu
141 ## STDOUT:
142 [nullglob]
143 ## END
144
145 #### compgen with action and suffix: helptopic
146 compgen -A helptopic -S ___ fa
147 ## STDOUT:
148 false___
149 ## END
150
151 #### compgen -A directory
152 # omit portable-files.mk
153 cd $REPO_ROOT
154 compgen -A directory p | sort
155 ## STDOUT:
156 pgen2
157 pylib
158 py-yajl
159 ## END
160
161 #### compgen -A file
162 cd $REPO_ROOT
163 compgen -A file b | sort
164 ## STDOUT:
165 benchmarks
166 bin
167 build
168 ## END
169
170 #### compgen -A user
171 # no assertion because this isn't hermetic
172 compgen -A user
173 ## status: 0
174
175 #### compgen -A command completes external commands
176 # NOTE: this test isn't hermetic
177 compgen -A command xarg | uniq
178 echo status=$?
179 ## STDOUT:
180 xargs
181 status=0
182 ## END
183
184 #### compgen -A command completes functions and aliases
185 my_func() { echo ; }
186 my_func2() { echo ; }
187 alias my_alias=foo
188 compgen -A command my_
189 echo status=$?
190 ## STDOUT:
191 my_alias
192 my_func
193 my_func2
194 status=0
195 ## END
196
197 #### compgen -A command completes builtins and keywords
198 compgen -A command eva
199 echo status=$?
200 compgen -A command whil
201 echo status=$?
202 ## STDOUT:
203 eval
204 status=0
205 while
206 status=0
207 ## END
208
209 #### complete with nonexistent function
210 complete -F invalidZZ -D
211 echo status=$?
212 ## stdout: status=2
213 ## BUG bash stdout: status=0
214
215 #### complete with no action
216 complete foo
217 echo status=$?
218 ## stdout: status=2
219 ## BUG bash stdout: status=0
220
221 #### -o filenames and -o nospace have no effect with compgen
222 # they are POSTPROCESSING.
223 compgen -o filenames -o nospace -W 'bin build'
224 ## STDOUT:
225 bin
226 build
227 ## END
228
229 #### -o plusdirs and -o dirnames with compgen
230 cd $REPO_ROOT
231 compgen -o plusdirs -W 'a b1 b2' b | sort
232 echo ---
233 compgen -o dirnames b | sort
234 ## STDOUT:
235 b1
236 b2
237 benchmarks
238 bin
239 build
240 ---
241 benchmarks
242 bin
243 build
244 ## END
245
246 #### compgen -o default completes files and dirs
247 cd $REPO_ROOT
248 compgen -o default spec/t | sort
249 ## STDOUT:
250 spec/testdata
251 spec/tilde.test.sh
252 spec/toysh-posix.test.sh
253 spec/toysh.test.sh
254 spec/type-compat.test.sh
255 ## END
256
257 #### compgen doesn't respect -X for user-defined functions
258 # WORKAROUND: wrap in bash -i -c because non-interactive bash behaves
259 # differently!
260 case $SH in
261 *bash|*osh)
262 $SH --rcfile /dev/null -i -c '
263 shopt -s extglob
264 fun() {
265 COMPREPLY=(one two three bin)
266 }
267 compgen -X "@(two|bin)" -F fun
268 echo --
269 compgen -X "!@(two|bin)" -F fun
270 '
271 esac
272 ## STDOUT:
273 one
274 three
275 --
276 two
277 bin
278 ## END
279
280 #### compgen -W words -X filter
281 # WORKAROUND: wrap in bash -i -c because non-interactive bash behaves
282 # differently!
283 case $SH in
284 *bash|*osh)
285 $SH --rcfile /dev/null -i -c 'shopt -s extglob; compgen -X "@(two|bin)" -W "one two three bin"'
286 esac
287 ## STDOUT:
288 one
289 three
290 ## END
291
292 #### compgen -f -X filter -- $cur
293 cd $TMP
294 touch spam.py spam.sh
295 compgen -f -- sp | sort
296 echo --
297 # WORKAROUND: wrap in bash -i -c because non-interactive bash behaves
298 # differently!
299 case $SH in
300 *bash|*osh)
301 $SH --rcfile /dev/null -i -c 'shopt -s extglob; compgen -f -X "!*.@(py)" -- sp'
302 esac
303 ## STDOUT:
304 spam.py
305 spam.sh
306 --
307 spam.py
308 ## END
309
310 #### compgen doesn't need shell quoting
311 # There is an obsolete comment in bash_completion that claims the opposite.
312 cd $TMP
313 touch 'foo bar'
314 touch "foo'bar"
315 compgen -f "foo b"
316 compgen -f "foo'"
317 ## STDOUT:
318 foo bar
319 foo'bar
320 ## END
321
322 #### compgen -W 'one two three'
323 cd $REPO_ROOT
324 compgen -W 'one two three'
325 echo --
326 compgen -W 'w1 w2 three' -A directory w
327 echo --
328 compgen -A directory -W 'w1 w2 three' w # order doesn't matter
329 ## STDOUT:
330 one
331 two
332 three
333 --
334 web
335 w1
336 w2
337 --
338 web
339 w1
340 w2
341 ## END
342
343 #### compgen -W evaluates code in $()
344 IFS=':%'
345 compgen -W '$(echo "spam:eggs%ham cheese")'
346 ## STDOUT:
347 spam
348 eggs
349 ham cheese
350 ## END
351
352 #### compgen -W uses IFS, and delimiters are escaped with \
353 IFS=':%'
354 compgen -W 'spam:eggs%ham cheese\:colon'
355 ## STDOUT:
356 spam
357 eggs
358 ham cheese:colon
359 ## END
360
361 #### Parse errors for compgen -W and complete -W
362 # bash doesn't detect as many errors because it lacks static parsing.
363 compgen -W '${'
364 echo status=$?
365 complete -W '${' foo
366 echo status=$?
367 ## STDOUT:
368 status=2
369 status=2
370 ## END
371 ## BUG bash STDOUT:
372 status=1
373 status=0
374 ## END
375
376 #### Runtime errors for compgen -W
377 compgen -W 'foo $(( 1 / 0 )) bar'
378 echo status=$?
379 ## STDOUT:
380 status=1
381 ## END
382
383 #### Runtime errors for compgen -F func
384 _foo() {
385 COMPREPLY=( foo bar )
386 COMPREPLY+=( $(( 1 / 0 )) ) # FATAL, but we still have candidates
387 }
388 compgen -F _foo foo
389 echo status=$?
390 ## STDOUT:
391 status=1
392 ## END
393
394 #### compgen -W '' cmd is not a usage error
395 # Bug fix due to '' being falsey in Python
396 compgen -W '' -- foo
397 echo status=$?
398 ## stdout: status=1