Skip to content

Commit d9a5f21

Browse files
author
rouault
committed
Add bash completion for GDAL/OGR utilities and scripts (contributed by Guillaume Pasero, #6381)
git-svn-id: https://svn.osgeo.org/gdal/trunk/gdal@33585 f0d54148-0727-0410-94bb-9a71ac55c965
1 parent a5ceb57 commit d9a5f21

5 files changed

+1024
-0
lines changed

GNUmakefile

+1
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ endif
211211
ifneq ($(BINDINGS),)
212212
(cd swig; $(MAKE) install)
213213
endif
214+
(cd scripts; $(MAKE) install)
214215
for f in LICENSE.TXT data/*.* ; do $(INSTALL_DATA) $$f $(DESTDIR)$(INST_DATA) ; done
215216
$(LIBTOOL_FINISH) $(DESTDIR)$(INST_LIB)
216217
$(INSTALL_DIR) $(DESTDIR)$(INST_LIB)/pkgconfig

HOWTO-RELEASE

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ Process :
3434
dev workstation) to avoid unnecessary churn from different autoconf or swig
3535
versions.
3636

37+
c) "cd scripts; make completion" to regenerate scripts/gdal-bash-completion.sh
38+
if new command line switches have been added. scripts/completionFinder.py
39+
must also be edited before if new utilities/scripts are added/removed.
40+
3741
2) Update the release date, and number information in gcore/gdal_version.h.
3842

3943
3) Update the VERSION file.

scripts/GNUmakefile

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
include ../GDALmake.opt
2+
3+
completion:
4+
PATH=$(GDAL_ROOT)/swig/python/scripts:$(GDAL_ROOT)/apps:$(PATH) python completionFinder.py gdal-bash-completion.sh
5+
6+
install:
7+
if test "x`pkg-config --version 2>/dev/null`" != "x" -a "x`pkg-config --variable=compatdir bash-completion`" != "x"; then \
8+
$(INSTALL_DIR) $(DESTDIR)`pkg-config --variable=compatdir bash-completion` ; \
9+
cp gdal-bash-completion.sh $(DESTDIR)`pkg-config --variable=compatdir bash-completion`; \
10+
fi

scripts/completionFinder.py

+287
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
###############################################################################
4+
# $Id$
5+
#
6+
# Project: GDAL/OGR Utilities
7+
# Purpose: Bash completion script generation
8+
# Author: Guillaume Pasero <guillaume dot pasero at c dash s dot fr>
9+
#
10+
###############################################################################
11+
# Copyright (c) 2016, Guillaume Pasero <guillaume dot pasero at c dash s dot fr>
12+
#
13+
# Permission is hereby granted, free of charge, to any person obtaining a
14+
# copy of this software and associated documentation files (the "Software"),
15+
# to deal in the Software without restriction, including without limitation
16+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
17+
# and/or sell copies of the Software, and to permit persons to whom the
18+
# Software is furnished to do so, subject to the following conditions:
19+
#
20+
# The above copyright notice and this permission notice shall be included
21+
# in all copies or substantial portions of the Software.
22+
#
23+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29+
# DEALINGS IN THE SOFTWARE.
30+
###############################################################################
31+
32+
import sys, os
33+
import os.path as op
34+
35+
from subprocess import call, Popen, PIPE, STDOUT
36+
37+
def showHelp():
38+
print "Usage : completionFinder.py output_script"
39+
40+
41+
def processLine(line):
42+
outList = []
43+
lvl1 = line.split(' ')
44+
lvl2 = []
45+
for item in lvl1:
46+
lvl2 += item.split('[')
47+
lvl3 = []
48+
for item in lvl2:
49+
lvl3 += item.split(']')
50+
lvl4 = []
51+
for item in lvl3:
52+
lvl4 += item.split('|')
53+
54+
for item in lvl4:
55+
if len(item) >= 2:
56+
if item[0] == '-':
57+
key = item
58+
# Handle special cases -key={A/B/C}
59+
if (key.count("={") == 1 and key[-1] == '}'):
60+
pair = key.split("=")
61+
choices = ((pair[1])[1:-1]).split('/')
62+
for c in choices:
63+
outList.append(pair[0]+"="+c)
64+
else:
65+
outList.append(key)
66+
67+
return outList
68+
69+
def processTool(toolName):
70+
command = [toolName, "-"]
71+
72+
process = Popen(command,stdout=PIPE,stderr=STDOUT)
73+
result = process.communicate()[0]
74+
lines = result.split('\n')
75+
index = 0
76+
77+
while index < len(lines):
78+
if (lines[index].find("Usage:") >= 0):
79+
break
80+
index += 1
81+
82+
options = []
83+
84+
while index < len(lines):
85+
cleanLine = lines[index].strip('\t\r ')
86+
if (len(cleanLine) == 0):
87+
break;
88+
options += processLine(cleanLine)
89+
index += 1
90+
91+
return options
92+
93+
def parseGDALGeneralOptions():
94+
command = ["gdalinfo", "--help-general"]
95+
process = Popen(command,stdout=PIPE,stderr=STDOUT)
96+
result = process.communicate()[0]
97+
lines = result.split('\n')
98+
index = 0
99+
options = []
100+
while index < len(lines):
101+
cleanLine = lines[index].strip('\t\r ')
102+
parts = cleanLine.split(':')
103+
if (parts[0].find('--') == 0):
104+
words = parts[0].split(' ')
105+
options.append(words[0])
106+
index += 1
107+
return options
108+
109+
def parseOGRGeneralOptions():
110+
command = ["ogr2ogr", "--help-general"]
111+
process = Popen(command,stdout=PIPE,stderr=STDOUT)
112+
result = process.communicate()[0]
113+
lines = result.split('\n')
114+
index = 0
115+
options = []
116+
while index < len(lines):
117+
cleanLine = lines[index].strip('\t\r ')
118+
parts = cleanLine.split(':')
119+
if (parts[0].find('--') == 0):
120+
words = parts[0].split(' ')
121+
options.append(words[0])
122+
index += 1
123+
return options
124+
125+
# generate completion script section for :
126+
# - name : the given gdal/ogr tool
127+
# - optList : option list
128+
def getCompletionScript(name,optList):
129+
output = []
130+
output.append("_"+name+"()\n")
131+
output.append("{\n")
132+
output.append(" local cur prev\n")
133+
output.append(" COMPREPLY=()\n")
134+
output.append(" _get_comp_words_by_ref cur prev\n")
135+
output.append(" case \"$cur\" in\n")
136+
output.append(" -*)\n")
137+
optLine = " key_list=\""
138+
for item in optList:
139+
optLine += item + " "
140+
optLine += "\"\n"
141+
output.append(optLine)
142+
output.append(" COMPREPLY=( $( compgen -W '$key_list' -- $cur) )\n")
143+
output.append(" return 0\n")
144+
output.append(" ;;\n")
145+
output.append(" esac\n")
146+
147+
# adapt format parsing command : GDAL type of OGR type
148+
149+
isGdal = True
150+
if (name.find("ogr") == 0):
151+
isGdal = False
152+
153+
# gdal type
154+
if isGdal:
155+
formatParsingCmd = "$tool --formats | tail -n +2 | cut -f 3 -d ' '"
156+
if ("-ot" in optList) or ("-of" in optList) or ("--format" in optList):
157+
output.append(" tool=${COMP_WORDS[0]}\n")
158+
output.append(" case \"$prev\" in\n")
159+
if "-ot" in optList:
160+
output.append(" -ot)\n")
161+
output.append(" key_list=\"Byte Int16 UInt16 UInt32 Int32 Float32 Float64 CInt16 CInt32 CFloat32 CFloat64\"\n")
162+
output.append(" COMPREPLY=( $( compgen -W '$key_list' -- $cur) )\n")
163+
output.append(" ;;\n")
164+
if ("-of" in optList) and isGdal:
165+
output.append(" -of)\n")
166+
output.append(" key_list=\"$( "+formatParsingCmd+")\"\n")
167+
output.append(" COMPREPLY=( $( compgen -W '$key_list' -- $cur) )\n")
168+
output.append(" ;;\n")
169+
if ("--format" in optList) and isGdal:
170+
output.append(" --format)\n")
171+
output.append(" key_list=\"$( "+formatParsingCmd+")\"\n")
172+
output.append(" COMPREPLY=( $( compgen -W '$key_list' -- $cur) )\n")
173+
output.append(" ;;\n")
174+
output.append(" esac\n")
175+
else:
176+
# ogr type
177+
formatParsingCmd = "$tool --formats | tail -n +2 | grep -o -E '\"[^\"]+\"' | sed 's/\ /__/'"
178+
if ("-f" in optList):
179+
# replace ogrtindex by ogr2ogr to check --formats
180+
output.append(" tool=${COMP_WORDS[0]/ogrtindex/ogr2ogr}\n")
181+
output.append(" case \"$prev\" in\n")
182+
if ("-f" in optList) and not isGdal:
183+
# completion is more tricky here because of spaces
184+
output.append(" -f)\n")
185+
output.append(" key_list=\"$( "+formatParsingCmd+")\"\n")
186+
output.append(" for iter in $key_list; do\n")
187+
output.append(" if [[ $iter =~ ^$cur ]]; then\n")
188+
output.append(" COMPREPLY+=( \"${iter//__/ }\" )\n")
189+
output.append(" fi\n")
190+
output.append(" done\n")
191+
output.append(" ;;\n")
192+
output.append(" esac\n")
193+
194+
output.append(" return 0\n")
195+
output.append("}\n")
196+
output.append("complete -o default -F _"+name+" "+name+"\n")
197+
198+
return output
199+
200+
def main(argv):
201+
if len(argv) < 2 :
202+
showHelp()
203+
return 1
204+
205+
gdaltools = [ "gdal2tiles.py",\
206+
"gdal2xyz.py",\
207+
"gdaladdo",\
208+
# "gdal_auth.py",\
209+
"gdalbuildvrt",\
210+
"gdal_calc.py",\
211+
"gdalchksum.py",\
212+
"gdalcompare.py",\
213+
"gdal-config",\
214+
"gdal_contour",\
215+
"gdaldem",\
216+
"gdal_edit.py",\
217+
"gdalenhance",\
218+
"gdal_fillnodata.py",\
219+
"gdal_grid",\
220+
"gdalident.py",\
221+
"gdalimport.py",\
222+
"gdalinfo",\
223+
"gdallocationinfo",\
224+
"gdalmanage",\
225+
"gdal_merge.py",\
226+
"gdalmove.py",\
227+
"gdal_polygonize.py",\
228+
"gdal_proximity.py",\
229+
"gdal_rasterize",\
230+
"gdal_retile.py",\
231+
"gdalserver",\
232+
"gdal_sieve.py",\
233+
"gdalsrsinfo",\
234+
"gdaltindex",\
235+
"gdaltransform",\
236+
"gdal_translate",\
237+
"gdalwarp"]
238+
239+
ogrtools = [ "ogr2ogr",\
240+
"ogrinfo",\
241+
"ogrlineref",\
242+
"ogrtindex"]
243+
244+
# parse general options
245+
generalOptions = parseGDALGeneralOptions()
246+
generalOGROptions = parseOGRGeneralOptions()
247+
248+
outFile = argv[1]
249+
of = open(outFile,'w')
250+
of.write("# File auto-generated by completionFinder.py, do not modify manually\n")
251+
of.write("""
252+
function_exists() {
253+
declare -f -F $1 > /dev/null
254+
return $?
255+
}
256+
257+
# Checks that bash-completion is recent enough
258+
function_exists _get_comp_words_by_ref || return 0
259+
260+
""")
261+
262+
for name in gdaltools:
263+
# verbose print
264+
print name
265+
optList = processTool(name)
266+
if "--help-general" in optList:
267+
for item in generalOptions:
268+
if not item in optList:
269+
optList.append(item)
270+
of.writelines(getCompletionScript(name,optList))
271+
for name in ogrtools:
272+
# verbose print
273+
print name
274+
optList = processTool(name)
275+
if "--help-general" in optList:
276+
for item in generalOGROptions:
277+
if not item in optList:
278+
optList.append(item)
279+
of.writelines(getCompletionScript(name,optList))
280+
281+
of.close()
282+
return 0
283+
284+
285+
if __name__ == "__main__":
286+
main(sys.argv)
287+

0 commit comments

Comments
 (0)