Skip to content

Commit

Permalink
Add bash completion for GDAL/OGR utilities and scripts (contributed b…
Browse files Browse the repository at this point in the history
…y Guillaume Pasero, #6381)

git-svn-id: https://svn.osgeo.org/gdal/trunk/gdal@33585 f0d54148-0727-0410-94bb-9a71ac55c965
  • Loading branch information
rouault committed Feb 28, 2016
1 parent a5ceb57 commit d9a5f21
Show file tree
Hide file tree
Showing 5 changed files with 1,024 additions and 0 deletions.
1 change: 1 addition & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ endif
ifneq ($(BINDINGS),)
(cd swig; $(MAKE) install)
endif
(cd scripts; $(MAKE) install)
for f in LICENSE.TXT data/*.* ; do $(INSTALL_DATA) $$f $(DESTDIR)$(INST_DATA) ; done
$(LIBTOOL_FINISH) $(DESTDIR)$(INST_LIB)
$(INSTALL_DIR) $(DESTDIR)$(INST_LIB)/pkgconfig
Expand Down
4 changes: 4 additions & 0 deletions HOWTO-RELEASE
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ Process :
dev workstation) to avoid unnecessary churn from different autoconf or swig
versions.

c) "cd scripts; make completion" to regenerate scripts/gdal-bash-completion.sh
if new command line switches have been added. scripts/completionFinder.py
must also be edited before if new utilities/scripts are added/removed.

2) Update the release date, and number information in gcore/gdal_version.h.

3) Update the VERSION file.
Expand Down
10 changes: 10 additions & 0 deletions scripts/GNUmakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
include ../GDALmake.opt

completion:
PATH=$(GDAL_ROOT)/swig/python/scripts:$(GDAL_ROOT)/apps:$(PATH) python completionFinder.py gdal-bash-completion.sh

install:
if test "x`pkg-config --version 2>/dev/null`" != "x" -a "x`pkg-config --variable=compatdir bash-completion`" != "x"; then \
$(INSTALL_DIR) $(DESTDIR)`pkg-config --variable=compatdir bash-completion` ; \
cp gdal-bash-completion.sh $(DESTDIR)`pkg-config --variable=compatdir bash-completion`; \
fi
287 changes: 287 additions & 0 deletions scripts/completionFinder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
###############################################################################
# $Id$
#
# Project: GDAL/OGR Utilities
# Purpose: Bash completion script generation
# Author: Guillaume Pasero <guillaume dot pasero at c dash s dot fr>
#
###############################################################################
# Copyright (c) 2016, Guillaume Pasero <guillaume dot pasero at c dash s dot fr>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
###############################################################################

import sys, os
import os.path as op

from subprocess import call, Popen, PIPE, STDOUT

def showHelp():
print "Usage : completionFinder.py output_script"


def processLine(line):
outList = []
lvl1 = line.split(' ')
lvl2 = []
for item in lvl1:
lvl2 += item.split('[')
lvl3 = []
for item in lvl2:
lvl3 += item.split(']')
lvl4 = []
for item in lvl3:
lvl4 += item.split('|')

for item in lvl4:
if len(item) >= 2:
if item[0] == '-':
key = item
# Handle special cases -key={A/B/C}
if (key.count("={") == 1 and key[-1] == '}'):
pair = key.split("=")
choices = ((pair[1])[1:-1]).split('/')
for c in choices:
outList.append(pair[0]+"="+c)
else:
outList.append(key)

return outList

def processTool(toolName):
command = [toolName, "-"]

process = Popen(command,stdout=PIPE,stderr=STDOUT)
result = process.communicate()[0]
lines = result.split('\n')
index = 0

while index < len(lines):
if (lines[index].find("Usage:") >= 0):
break
index += 1

options = []

while index < len(lines):
cleanLine = lines[index].strip('\t\r ')
if (len(cleanLine) == 0):
break;
options += processLine(cleanLine)
index += 1

return options

def parseGDALGeneralOptions():
command = ["gdalinfo", "--help-general"]
process = Popen(command,stdout=PIPE,stderr=STDOUT)
result = process.communicate()[0]
lines = result.split('\n')
index = 0
options = []
while index < len(lines):
cleanLine = lines[index].strip('\t\r ')
parts = cleanLine.split(':')
if (parts[0].find('--') == 0):
words = parts[0].split(' ')
options.append(words[0])
index += 1
return options

def parseOGRGeneralOptions():
command = ["ogr2ogr", "--help-general"]
process = Popen(command,stdout=PIPE,stderr=STDOUT)
result = process.communicate()[0]
lines = result.split('\n')
index = 0
options = []
while index < len(lines):
cleanLine = lines[index].strip('\t\r ')
parts = cleanLine.split(':')
if (parts[0].find('--') == 0):
words = parts[0].split(' ')
options.append(words[0])
index += 1
return options

# generate completion script section for :
# - name : the given gdal/ogr tool
# - optList : option list
def getCompletionScript(name,optList):
output = []
output.append("_"+name+"()\n")
output.append("{\n")
output.append(" local cur prev\n")
output.append(" COMPREPLY=()\n")
output.append(" _get_comp_words_by_ref cur prev\n")
output.append(" case \"$cur\" in\n")
output.append(" -*)\n")
optLine = " key_list=\""
for item in optList:
optLine += item + " "
optLine += "\"\n"
output.append(optLine)
output.append(" COMPREPLY=( $( compgen -W '$key_list' -- $cur) )\n")
output.append(" return 0\n")
output.append(" ;;\n")
output.append(" esac\n")

# adapt format parsing command : GDAL type of OGR type

isGdal = True
if (name.find("ogr") == 0):
isGdal = False

# gdal type
if isGdal:
formatParsingCmd = "$tool --formats | tail -n +2 | cut -f 3 -d ' '"
if ("-ot" in optList) or ("-of" in optList) or ("--format" in optList):
output.append(" tool=${COMP_WORDS[0]}\n")
output.append(" case \"$prev\" in\n")
if "-ot" in optList:
output.append(" -ot)\n")
output.append(" key_list=\"Byte Int16 UInt16 UInt32 Int32 Float32 Float64 CInt16 CInt32 CFloat32 CFloat64\"\n")
output.append(" COMPREPLY=( $( compgen -W '$key_list' -- $cur) )\n")
output.append(" ;;\n")
if ("-of" in optList) and isGdal:
output.append(" -of)\n")
output.append(" key_list=\"$( "+formatParsingCmd+")\"\n")
output.append(" COMPREPLY=( $( compgen -W '$key_list' -- $cur) )\n")
output.append(" ;;\n")
if ("--format" in optList) and isGdal:
output.append(" --format)\n")
output.append(" key_list=\"$( "+formatParsingCmd+")\"\n")
output.append(" COMPREPLY=( $( compgen -W '$key_list' -- $cur) )\n")
output.append(" ;;\n")
output.append(" esac\n")
else:
# ogr type
formatParsingCmd = "$tool --formats | tail -n +2 | grep -o -E '\"[^\"]+\"' | sed 's/\ /__/'"
if ("-f" in optList):
# replace ogrtindex by ogr2ogr to check --formats
output.append(" tool=${COMP_WORDS[0]/ogrtindex/ogr2ogr}\n")
output.append(" case \"$prev\" in\n")
if ("-f" in optList) and not isGdal:
# completion is more tricky here because of spaces
output.append(" -f)\n")
output.append(" key_list=\"$( "+formatParsingCmd+")\"\n")
output.append(" for iter in $key_list; do\n")
output.append(" if [[ $iter =~ ^$cur ]]; then\n")
output.append(" COMPREPLY+=( \"${iter//__/ }\" )\n")
output.append(" fi\n")
output.append(" done\n")
output.append(" ;;\n")
output.append(" esac\n")

output.append(" return 0\n")
output.append("}\n")
output.append("complete -o default -F _"+name+" "+name+"\n")

return output

def main(argv):
if len(argv) < 2 :
showHelp()
return 1

gdaltools = [ "gdal2tiles.py",\
"gdal2xyz.py",\
"gdaladdo",\
# "gdal_auth.py",\
"gdalbuildvrt",\
"gdal_calc.py",\
"gdalchksum.py",\
"gdalcompare.py",\
"gdal-config",\
"gdal_contour",\
"gdaldem",\
"gdal_edit.py",\
"gdalenhance",\
"gdal_fillnodata.py",\
"gdal_grid",\
"gdalident.py",\
"gdalimport.py",\
"gdalinfo",\
"gdallocationinfo",\
"gdalmanage",\
"gdal_merge.py",\
"gdalmove.py",\
"gdal_polygonize.py",\
"gdal_proximity.py",\
"gdal_rasterize",\
"gdal_retile.py",\
"gdalserver",\
"gdal_sieve.py",\
"gdalsrsinfo",\
"gdaltindex",\
"gdaltransform",\
"gdal_translate",\
"gdalwarp"]

ogrtools = [ "ogr2ogr",\
"ogrinfo",\
"ogrlineref",\
"ogrtindex"]

# parse general options
generalOptions = parseGDALGeneralOptions()
generalOGROptions = parseOGRGeneralOptions()

outFile = argv[1]
of = open(outFile,'w')
of.write("# File auto-generated by completionFinder.py, do not modify manually\n")
of.write("""
function_exists() {
declare -f -F $1 > /dev/null
return $?
}
# Checks that bash-completion is recent enough
function_exists _get_comp_words_by_ref || return 0
""")

for name in gdaltools:
# verbose print
print name
optList = processTool(name)
if "--help-general" in optList:
for item in generalOptions:
if not item in optList:
optList.append(item)
of.writelines(getCompletionScript(name,optList))
for name in ogrtools:
# verbose print
print name
optList = processTool(name)
if "--help-general" in optList:
for item in generalOGROptions:
if not item in optList:
optList.append(item)
of.writelines(getCompletionScript(name,optList))

of.close()
return 0


if __name__ == "__main__":
main(sys.argv)

Loading

0 comments on commit d9a5f21

Please sign in to comment.