From a59e04ff3669d3e160f96464567f758b068ec562 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Raphael=20D=C3=BCmig?= Date: Wed, 8 Oct 2014 14:36:39 +0200 Subject: [PATCH] replaced the outdated bash completion script with a completely rewritten version The new script is documented in the flightgear wiki (see "bash completion") and adds functionality such as advanced detection of FG_ROOT and other paths, and many completions not available before. The list of possible command line options is automatically generated from "fgfs --help --verbose". The author of the original script (mfranz) has agreed to replace this script. --- scripts/completion/fg-completion.bash | 654 +++++++++++++++----------- 1 file changed, 380 insertions(+), 274 deletions(-) diff --git a/scripts/completion/fg-completion.bash b/scripts/completion/fg-completion.bash index fe55f1679..4bd70f35a 100755 --- a/scripts/completion/fg-completion.bash +++ b/scripts/completion/fg-completion.bash @@ -1,291 +1,397 @@ -#!/bin/bash -# Tab completion for FlightGear command line options. To use it put -# this in your ~/.bashrc file: -# -# export FG_HOME=$HOME/.fgfs/ -# [ -e $FG_HOME/fg-completion.bash ] && source $FG_HOME/fg-completion.bash -# -# Defining FG_HOME is only required if you don't like the default -# "$HOME/.fgfs/". The script doesn't offer all available airports -# for the --airport option, but only those listed in a file -# $FG_HOME/airport.list if available, or a short default list otherwise. -# -# After installing new aircraft you have to rebuild the aircraft list -# by typing $ fgfs --aircraft=? +# © 2014 Raphael Dümig ( duemig at in dot tum dot de ) forum handle: "hamster" +# distributed under the terms of the GPLv3 -__fgfs_root=${FG_ROOT:-/usr/local/share/FlightGear} -__fgfs_home=${FG_HOME:-$HOME/.fgfs} -[ -d "$__fgfs_home" ] || mkdir -p "$__fgfs_home" +local_config_file="$HOME/.fgfsrc" - -__fgfs_ac_list="$__fgfs_home/aircraft.list" -__fgfs_apt_list="$__fgfs_home/airport.list" - -__fgfs_options=" - --help - --verbose - --disable-intro-music - --enable-intro-music - --units-feet - --units-meters - --disable-sound - --enable-sound - --disable-panel - --enable-panel - --disable-hud - --enable-hud - --disable-anti-alias-hud - --enable-anti-alias-hud - --disable-hud-3d - --enable-hud-3d - --hud-tris - --hud-culled - --disable-random-objects - --enable-random-objects - --disable-ai-models - --enable-ai-models - --disable-freeze - --enable-freeze - --disable-fuel-freeze - --enable-fuel-freeze - --disable-clock-freeze - --enable-clock-freeze - --disable-splash-screen - --enable-splash-screen - --disable-mouse-pointer - --enable-mouse-pointer - --fog-disable - --fog-fastest - --fog-nicest - --disable-enhanced-lighting - --enable-enhanced-lighting - --disable-distance-attenuation - --enable-distance-attenuation - --disable-specular-highlight - --enable-specular-highlight - --disable-fullscreen - --enable-fullscreen - --disable-game-mode - --enable-game-mode - --shading-flat - --shading-smooth - --disable-skyblend - --enable-skyblend - --disable-textures - --enable-textures - --disable-wireframe - --enable-wireframe - --notrim - --on-ground - --in-air - --enable-auto-coordination - --disable-auto-coordination - --show-aircraft - --time-match-real - --time-match-local - --disable-real-weather-fetch - --enable-real-weather-fetch - --disable-horizon-effect - --enable-horizon-effect - --enable-clouds - --disable-clouds - --enable-clouds3d - --disable-clouds3d - --atc610x - --enable-save-on-exit - --disable-save-on-exit - --ai-scenario= - --fg-root= - --fg-scenery= - --language= - --browser-app= - --config= - --failure= - --bpp= - --fov= - --callsign= - --aspect-ratio-multiplier= - --geometry= - --view-offset= - --aircraft= - --min-status= - --fdm= - --aero= - --model-hz= - --speed= - --aircraft-dir= - --timeofday= - --time-offset= - --start-date-sys= - --start-date-gmt= - --start-date-lat= - --airport= - --runway= - --carrier= - --parkpos= - --vor= - --ndb= - --fix= - --offset-distance= - --offset-azimuth= - --lon= - --lat= - --altitude= - --heading= - --roll= - --pitch= - --uBody= - --vBody= - --wBody= - --vc= - --mach= - --glideslope= - --roc= - --wp= - --flight-plan= - --nav1= - --nav2= - --adf= - --dme= - --visibility= - --visibility-miles= - --wind= - --turbulence= - --ceiling= - --multiplay= - --proxy= - --httpd= - --telnet= - --jpg-httpd= - --generic= - --garmin= - --joyclient= - --jsclient= - --native-ctrls= - --native-fdm= - --native= - --nmea= - --opengc= - --props= - --pve= - --ray= - --rul= - --log-level= - --trace-read= - --trace-write= - --season= - --vehicle= - --prop: -" - - -if [ ${BASH_VERSINFO[0]} -eq 2 ] && [[ ${BASH_VERSINFO[1]} = "05b" ]] \ - || [ ${BASH_VERSINFO[0]} -gt 2 ]; then - __fgfs_nospace="-o nospace" -fi - -shopt -s progcomp - - -__fgfs_write_ac_list() { - rm -f $__fgfs_ac_list - for i in $__fgfs_root/Aircraft/*/*-set.xml; do - i=${i%-set.xml} - echo ${i##*/} >>$__fgfs_ac_list - done +_get_opt() +{ + local opt len + opt=$1 + len=`expr ${#opt} + 3` + + if [ -e "$local_config_file" ] + then + stored_opts="$(cat "$local_config_file")" + fi + + for word in $stored_opts "${words[@]}" + do + if [ "${word:0:$len}" == "--$opt=" ] + then + # TODO: we have to get rid of at least the most important escape sequences!!! + value="$(echo "${word:$len}" | sed 's/\\ / /g')" + fi + done } - -[ -e $__fgfs_ac_list ] || __fgfs_write_ac_list - - -__fgfs_ai_scenario() { - local i - for i in $__fgfs_root/AI/*.xml; do - i=${i%.xml} - echo ${i##*/} - done +_get_opts() +{ + local opt len + opt=$1 + len=`expr ${#opt} + 3` + value="" + + if [ -e "$local_config_file" ] + then + stored_opts="$(cat "$local_config_file")" + fi + + for word in "$stored_opts ${words[@]}" + do + if [ "${word:0:$len}" == "--$opt=" ] + then + value+="$(echo "${word:$len}" | sed 's/\\ / /g') " + fi + done } - -__fgfs_aircraft() { - while read i; do - echo $i - done <$__fgfs_ac_list +_get_fg_root() +{ + local value + _get_opt "fg-root" + + # value on the command line? + if [ -n "$value" ] + then + root="$value" + return 0 + # value stored in environment variable $FG_ROOT? + else if [ -n "$FG_ROOT" ] + then + root="$FG_ROOT" + return 0 + # no clue! search at the most common places + else + for path in /usr{/local,}/share{/games,}/{F,f}light{G,g}ear{/data,} {~,}/Applications/FlightGear.app/Contents/Resources/data + do + if [ -e "$path" ] + then + root="$path" + break + fi + done + fi + fi } - -__fgfs_offer() { - local i - for i in "$@"; do - [ "$i" == "${i%=}" ] && i="$i " - echo "$i" - done +_get_fg_scenery() +{ + local value + _get_opt "fg-scenery" + + # value on command line? + if [ -n "$value" ] + then + scenery="$value" + # value stored in environment variable $FG_SCENERY? + else if [ -n "$FG_SCENERY" ] + then + scenery="$FG_SCENERY" + # no clue! try default: + else + local root + _get_fg_root + scenery="$root/Scenery" + fi + fi + + return 0 } +_get_fg_aircraft() +{ + local value + _get_opt "fg-aircraft" + + # value on command line? + if [ -n "$value" ] + then + aircraft_dir="$value" + # value stored in environment variable $FG_AIRCRAFT? + else if [ -n "$FG_AIRCRAFT" ] + then + aircraft_dir="$FG_AIRCRAFT" + # no clue! try default: + else + local root + _get_fg_root + aircraft_dir="$root/Aircraft" + fi + fi +} -__fgfs_options=$(__fgfs_offer $__fgfs_options) - - -__fgfs() { - COMPREPLY=() - local IFS=$'\n'$'\t' cur=${COMP_WORDS[COMP_CWORD]} alt +_get_airport() +{ + local value + _get_opt "airport" + + if [ -z "$value" ] + then + airport="KSFO" + else + airport=$value + fi +} - case "$cur" in - --ai-scenario=*) - alt=$(__fgfs_offer $(__fgfs_ai_scenario)) - ;; - --aircraft=\?) - __fgfs_write_ac_list - ;; - --aircraft=*|--vehicle=*) - alt=$(__fgfs_offer $(__fgfs_aircraft)) - ;; - --airport=*) - if [ -e "$__fgfs_apt_list" ]; then - alt=$(cat "$__fgfs_apt_list") - else - alt=$(__fgfs_offer khaf kpao koak kmry knuq ksjc kccr ksns krhv klvk o62 lpma) - fi - ;; - --carrier=*) - alt=$(__fgfs_offer Nimitz Eisenhower Foch) - ;; - --failure=*) - alt=$(__fgfs_offer pitot static vacuum electrical) - ;; - --fdm=*) - alt=$(__fgfs_offer jsbsim yasim uiuc larcsim ufo magic) - ;; - --geometry=*) - alt=$(__fgfs_offer 640x480 800x600 1024x768 1152x864 1600x1200) - ;; - --log-level=*) - alt=$(__fgfs_offer bulk debug info warn alert) - ;; - --min-status=*) - alt=$(__fgfs_offer alpha beta early-production production) - ;; - --parkpos=*) - alt=$(__fgfs_offer cat-1 cat-2 cat-3 cat-4 park-1) - ;; - --season=*) - alt=$(__fgfs_offer summer winter) - ;; - --timeofday=*) - alt=$(__fgfs_offer real dawn morning noon afternoon dusk evening midnight) - ;; - --prop:*) - return - ;; - *) - alt="$__fgfs_options" - ;; - esac +_get_carrier() +{ + local value + _get_opt "carrier" + + carrier=$value +} - COMPREPLY=($(compgen -W "$alt" -- ${cur#*=})) +_get_scenarios() +{ + local value + _get_opts "ai-scenario" + + scenarios="$value nimitz_demo" } -complete -o default $__fgfs_nospace -F __fgfs fgfs signs fgfsterra +_fgfs() +{ + local cur prev words cword split + _init_completion -s || return + local root airport aircraft_dir carrier scenarios scenery value + + # auto-completion for values of keys ( --key=value ) + case "$prev" in + --fg-aircraft|--fg-root|--fg-scenery|--flight-plan|--terrasync-dir|--materials-file|--config|--browser-app) + # completion of filesystem path + _filedir + return 0 ;; + --ai-scenario) + # list of scenarios in $FG_ROOT/AI + _get_fg_root + scenarios="$(find "$root/AI" -maxdepth 1 -iname *.xml -printf '%f\n' | sed -e 's/.xml$//')" + + COMPREPLY=( $(compgen -W "${scenarios}" -- ${cur}) ) + return 0 ;; + --aircraft|--vehicle) + # list of aircrafts in $FG_AIRCRAFT + _get_fg_aircraft + aircrafts="$(find "$aircraft_dir" -maxdepth 2 -mindepth 2 -iname *-set.xml -printf '%f\n' | sed -e 's/-set.xml$//')" + + COMPREPLY=( $(compgen -W "${aircrafts}" -- ${cur}) ) + return 0 ;; + --airport) + _get_fg_root + _get_fg_scenery + # is an index file present in the scenery? + if [ -e "$scenery/Airports/index.txt" ] + then + COMPREPLY=( $(compgen -W "$(awk 'BEGIN { FS="|"; } { print $1 }' "$scenery/Airports/index.txt")" -- ${cur}) ) + return 0 + # or at least the apt.dat file? + else if [ -e "$root/Airports/apt.dat.gz" ] + then + COMPREPLY=( $(compgen -W "$(zcat "$root/Airports/apt.dat.gz" | awk '/^1 / { print $5 }')" -- ${cur}) ) + return 0 + fi + fi + + return 0 ;; + --carrier) + _get_fg_root + _get_scenarios + + carriers="" + + for scenario in $scenarios + do + carriers+="$(awk -- ' +BEGIN { carrier=0; name=""; } +// { + a=index($0,"")+6; s=index(substr($0,a),"")-1; + if(substr($0,a,s) == "carrier") carrier=1; +} +// { + if(carrier) { + a=index($0,"")+6; s=index(substr($0,a),"")-1; + print substr($0,a,s); + carrier=0; + } +} +/<\/entry>/ { + carrier=0; + name=""; +}' "$root/AI/$scenario.xml") " + done + + COMPREPLY=( $(compgen -W "${carriers}" ${cur}) ) + return 0 ;; + --runway) + _get_fg_scenery + _get_airport + + # try to find a thresholds file + path="$scenery/Airports/${airport:0:1}/${airport:1:1}/${airport:2:1}" + + if [ -e "$path/$airport.threshold.xml" ] + then + runways="$(awk -- ' +// { + a=index($0,"")+5; + s=index(substr($0,a),"")-1; + print substr($0,a,s) +}' "$path/$airport.threshold.xml")" + fi + + COMPREPLY=( $(compgen -W "${runways}" -- ${cur}) ) + return 0 ;; + --parkpos) + # try to find out the name of the carrier or the ID of the airport + _get_carrier + + if [ -n "$carrier" ] + then + _get_fg_root + _get_scenarios + + for scenario in $scenarios + do + positions="$(awk -v carrier_name="$carrier" ' +BEGIN { carrier=0; name=0; parkpos=0; } +// { + a=index($0,"")+6; s=index(substr($0,a),"")-1; + if(substr($0,a,s) == "carrier") carrier=1; +} +// { + parkpos=(carrier && name); +} +// { + a=index($0,"")+6; s=index(substr($0,a),"")-1; + if(parkpos) print substr($0,a,s); + else if(carrier) name=(substr($0,a,s) == carrier_name); +} +/<\/parking-pos>/ { + parkpos=0; +} +/<\/entry>/ { + carrier=name=parkpos=0; +}' "$root/AI/$scenario.xml")" + + if [ -n "$positions" ] + then + break + fi + done + + else + _get_fg_scenery + _get_airport + + # search for the groundnet or parking file + path="$scenery/Airports/${airport:0:1}/${airport:1:1}/${airport:2:1}" + + if [ -e "$path/$airport.groundnet.xml" ] + then + file="$airport.groundnet.xml" + else if [ -e "$path/$airport.parking.xml" ] + then + file="$airport.parking.xml" + else + # no file found => do not try to analyze it! + return 0 + fi + fi + + # build a list of the parking positions at that airport + positions="$(awk -- ' +// { + if(pos_active == 1 && (name!="" || number!="")) print name number; + pos_active=0; + name=number=""; +}' "$path/$file")" + fi + + COMPREPLY=( $(compgen -W "${positions}" -- ${cur}) ) + return 0 ;; + --control) + COMPREPLY=( $(compgen -W "joystick keyboard mouse" -- ${cur}) ) + return 0 ;; + --failure) + COMPREPLY=( $(compgen -W "pitot static vacuum electrical" -- ${cur}) ) + return 0 ;; + --fix) + _get_fg_root + COMPREPLY=( $(compgen -W "$(zcat "$root/Navaids/fix.dat.gz" | awk '{ print substr($3,0,5) }')" -- ${cur}) ) + return 0 ;; + --fdm) + COMPREPLY=( $(compgen -W "jsb larcsim yasim magic balloon ada external null" -- ${cur}) ) + return 0 ;; + --min-status) + COMPREPLY=( $(compgen -W "alpha beta early-production production" -- ${cur}) ) + return 0 ;; + --log-class) + COMPREPLY=( $(compgen -W "ai environment flight general io network sound terrain" -- ${cur}) ) + return 0 ;; + --log-level) + COMPREPLY=( $(compgen -W "bulk debug info warn alert" -- ${cur}) ) + return 0 ;; + --ndb) + _get_fg_root + COMPREPLY=( $(compgen -W "$(zcat "$root/Navaids/nav.dat.gz" | awk '/^2 / { print $8 }')" -- ${cur}) ) + return 0 ;; + --ndb-frequency) + _get_fg_root + _get_opt "ndb" + COMPREPLY=( $(compgen -W "$(zcat "$root/Navaids/nav.dat.gz" | awk -v nav=$value '/^2 / { if($8 == nav) print $5 }')" -- ${cur}) ) + return 0 ;; + --season) + COMPREPLY=( $(compgen -W "summer winter" -- ${cur}) ) + return 0 ;; + --texture-filtering) + COMPREPLY=( $(compgen -W "1 2 4 8 16" -- ${cur}) ) + return 0 ;; + --timeofday) + COMPREPLY=( $(compgen -W "real dawn morning noon afternoon dusk evening midnight" -- ${cur}) ) + return 0 ;; + --view-offset) + COMPREPLY=( $(compgen -W "LEFT RIGHT CENTER" -- ${cur}) ) + return 0 ;; + --vor) + _get_fg_root + COMPREPLY=( $(compgen -W "$(zcat "$root/Navaids/nav.dat.gz" | awk '/^3 / { print $8 }')" -- ${cur}) ) + return 0 ;; + --vor-frequency) + _get_fg_root + _get_opt "vor" + COMPREPLY=( $(compgen -W "$(zcat "$root/Navaids/nav.dat.gz" | awk -v nav=$value '/^3 / { if($8 == nav) print substr($5,0,3) "." substr($5,4) }')" -- ${cur}) ) + return 0 ;; + esac + + case "${cur}" in + -*) + # auto completion for options + COMPREPLY=( $(compgen -W "$(_parse_help fgfs "--help --verbose")" -- ${cur}) ) + # no whitespace after keys + [[ $COMPREPLY == *= ]] && compopt -o nospace ;; + *) + _filedir + esac + + return 0 +} +complete -F _fgfs fgfs -- 2.39.5