-#!/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=?<TAB>
+# © 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=""; }
+/<type>/ {
+ a=index($0,"<type>")+6; s=index(substr($0,a),"</type>")-1;
+ if(substr($0,a,s) == "carrier") carrier=1;
+}
+/<name>/ {
+ if(carrier) {
+ a=index($0,"<name>")+6; s=index(substr($0,a),"</name>")-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 -- '
+/<rwy>/ {
+ a=index($0,"<rwy>")+5;
+ s=index(substr($0,a),"</rwy>")-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; }
+/<type>/ {
+ a=index($0,"<type>")+6; s=index(substr($0,a),"</type>")-1;
+ if(substr($0,a,s) == "carrier") carrier=1;
+}
+/<parking-pos>/ {
+ parkpos=(carrier && name);
+}
+/<name>/ {
+ a=index($0,"<name>")+6; s=index(substr($0,a),"</name>")-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 -- '
+/<Parking/ {
+ pos_active=1;
+ name=number="";
+}
+/name="/ {
+ if(pos_active) {
+ a=index($0,"name=\"")+6;
+ s=index(substr($0,a),"\"")-1;
+ name=substr($0,a,s);
+ }
+}
+/number="/ {
+ if(pos_active) {
+ a=index($0,"number=\"")+8;
+ s=index(substr($0,a),"\"")-1;
+ number=substr($0,a,s);
+ }
+}
+/\/>/ {
+ 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