first commit
This commit is contained in:
113
scripts/tools/fg-check
Executable file
113
scripts/tools/fg-check
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Checks source code and data for potential problems.
|
||||
# Meant to be executed before submitting/committing.
|
||||
|
||||
|
||||
SELF=${0##*/}
|
||||
|
||||
# optional apps
|
||||
RLE=$(which rle 2>/dev/null) # http://members.aon.at/mfranz/rle.tar.gz (depends on Qt lib)
|
||||
AC3D_SCAN=$(which ac3d-scan 2>/dev/null) # http://members.aon.at/mfranz/ac3d-scan
|
||||
|
||||
|
||||
function ERROR { echo -e "\e[31;1m$*\e[m"; }
|
||||
function LOG { echo -e "\e[35m$*\e[m"; }
|
||||
function RESULT { echo -e "\t$*"; }
|
||||
|
||||
|
||||
TMP=$(mktemp -d -t $SELF.XXX) || (echo "$0: can't create temporary dir"; exit 1)
|
||||
trap "rm -rf $TMP" 0 1 2 3 13 15
|
||||
|
||||
|
||||
LOG "checking for spaces in filenames ..."
|
||||
find .|grep " "|while read i; do RESULT "$i"; done
|
||||
|
||||
|
||||
LOG "checking for upper-case extensions ..."
|
||||
find .|while read i; do
|
||||
case "$i" in .|..|CVS/*|*/CVS/*|*.Opt|*.README|*.Po|*.TXT) continue ;; esac
|
||||
base=${i##*/}
|
||||
ext=${base##*.}
|
||||
[ "$base" == "$ext" ] && continue # has no extension
|
||||
ext=${ext//[^a-zA-Z]/}
|
||||
[ "$ext" ] || continue # only non-letters
|
||||
lcext=$(echo $ext|sed -e 's,\(.*\),\L\1,')
|
||||
[ "$ext" != "$lcext" ] && RESULT "$i"
|
||||
done
|
||||
|
||||
|
||||
LOG "checking for DOS line endings ..."
|
||||
find . -type f|while read i; do
|
||||
desc=$(file -b "$i")
|
||||
case "$desc" in *text*)
|
||||
grep "
|
||||
$" "$i" >/dev/null && RESULT "$i"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
LOG "checking for uncompressed textures ..."
|
||||
find . -iname \*.rgb -o -iname \*.rgba|while read i; do
|
||||
new=$TMP/sgi.rgb
|
||||
if [ -x "$RLE" ]; then
|
||||
cp "$i" $new && "$RLE" $new 2>&1|grep corrupt &>/dev/null && ERROR "\t$i ... FILE CORRUPTED"
|
||||
else
|
||||
convert "$i" -compress RLE sgi:$new
|
||||
fi
|
||||
perl -e '
|
||||
my $file = shift;
|
||||
my $old = -s $file;
|
||||
my $new = -s shift;
|
||||
if ($new < $old) {
|
||||
printf "\t$file: could be %0.02f%% of current size (%d bytes less)\n",
|
||||
100 * $new / $old, $old - $new;
|
||||
}
|
||||
' "$i" $new
|
||||
done
|
||||
|
||||
|
||||
if [ -x "$AC3D_SCAN" ]; then
|
||||
LOG "checking for AC3D sanity ..."
|
||||
find . -iname \*.ac|while read i; do
|
||||
case "$i" in configure.ac|*/configure.ac) continue ;; esac
|
||||
result=$($AC3D_SCAN <$i 2>&1)
|
||||
[ "$result" ] && echo -e "$result\n\t... in file \e[36m$i\e[m";
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
LOG "checking for thumbnail file size (expected JPEG 171x128)"
|
||||
find . -name thumbnail.jpg|while read i; do
|
||||
id=$(identify "$i")
|
||||
if ! echo $id|grep "JPEG 171x128" >/dev/null; then
|
||||
RESULT "$i ... $id"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
LOG "checking for 'userarchive' flags (not allowed in aircraft XML files) ..."
|
||||
find . -name \*.xml|while read i; do
|
||||
if grep "userarchive" $i >/dev/null; then
|
||||
RESULT "$i"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
LOG "checking for XML syntax ..."
|
||||
find . -name \*.xml|while read i; do
|
||||
xmllint $i >/dev/null || RESULT "... in file \e[36m$i\e[m"
|
||||
done
|
||||
|
||||
|
||||
LOG "checking for 'if (foo) delete foo;' ..."
|
||||
find . -iregex ".*\.\([ch]\(xx\|pp\)\|cc\|h\)$"|while read i; do perl -e '
|
||||
my $i = 0;
|
||||
my $name = $ARGV[0];
|
||||
undef $/;
|
||||
$_ = <>;
|
||||
s/(if\s*\(([^\)]+)\)\s*delete(?:\s+|\s*\[\]\s*)\2\s*;)/print "$1\n" and $i++/ges;
|
||||
print "\t... \033[36min file $name\033[m\n" if $i;
|
||||
' "$i"; done
|
||||
|
||||
366
scripts/tools/fg-submit
Executable file
366
scripts/tools/fg-submit
Executable file
@@ -0,0 +1,366 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This script called in a CVS directory compares local files with
|
||||
# the repository, and prepares an update package containing all
|
||||
# changes and new files for submission to a CVS maintainer. If there
|
||||
# are only changes in text files, then a compressed unified diff is
|
||||
# made (foo.diff.bz2). If there are also changed binary or new files,
|
||||
# then an archive is made instead (foo.tar.bz2). The base name ("foo")
|
||||
# can be given as command line argument. Otherwise the directory name
|
||||
# is used. The script also leaves a diff in uncompressed/unpackaged
|
||||
# form. This is only for developer convenience -- for a quick check
|
||||
# of the diff correctness. It is not to be submitted. The script will
|
||||
# not overwrite any file, but rather rename conflicting files.
|
||||
#
|
||||
# Usage: fg-submit [-v] [<basename>]
|
||||
#
|
||||
# Options:
|
||||
# -v ... verbose output
|
||||
#
|
||||
# Example:
|
||||
# $ cd $FG_ROOT/Aircraft/bo105
|
||||
# $ fg-submit # -> bo105.diff.bz2 or bo105.tar.bz2
|
||||
#
|
||||
# $ fg-submit update # -> update.diff.bz2 or update.tar.bz2
|
||||
#
|
||||
#
|
||||
# Spaces in the basename are replaced with "%20". People who prefer
|
||||
# to have the date in the archive name can conveniently achieve this
|
||||
# by defining a shell alias in ~/.bashrc:
|
||||
#
|
||||
# alias submit='fg-submit "${PWD##*/}-$(date +%Y-%m-%d)"'
|
||||
#
|
||||
#
|
||||
#
|
||||
# If the script finds an application named "fg-upload", then it calls
|
||||
# this at the end with two arguments:
|
||||
#
|
||||
# $1 ... archive or compressed diff for submission
|
||||
# $2 ... accessory uncompressed diff, *NOT* for submission!
|
||||
#
|
||||
# $1 and $2 are guaranteed not to contain spaces, only $1 is guaranteed
|
||||
# to actually exist. Such a script can be used to upload the file to an
|
||||
# ftp-/webserver, and/or to remove one or both files. Example using
|
||||
# KDE's kfmclient for upload (alternatives: ncftpput, gnomevfs-copy):
|
||||
#
|
||||
# $ cat ~/bin/fg-upload
|
||||
# #!/bin/bash
|
||||
# echo "uploading $1"
|
||||
# if kfmclient copy $1 ftp://user:password@server.com; then
|
||||
# echo "deleting $1 $2"
|
||||
# rm -f $1 $2
|
||||
#
|
||||
# URL=ftp://server.com/$1
|
||||
#
|
||||
# # copy URL to KDE's clipboard, so that MMB-clicking pastes it
|
||||
# dcop klipper klipper setClipboardContents $URL
|
||||
#
|
||||
# echo "Done. --> $URL"
|
||||
# else
|
||||
# echo "$0: uploading failed!"
|
||||
# fi
|
||||
#
|
||||
#
|
||||
#
|
||||
# Whether a file should be included in the archive or not, is decided
|
||||
# by pattern rules. There is a set of reasonable default rules predefined,
|
||||
# but alternative settings can be defined in a hidden configuration file
|
||||
# named ".fg-submit". Such a file is searched in the current directory,
|
||||
# in its parent directory, in its grand-parent directory and so on,
|
||||
# and finally in the $HOME directory. The first found file is taken.
|
||||
#
|
||||
# A file can use a list of three keywords with arguments, each on a
|
||||
# separate line:
|
||||
#
|
||||
# ALLOW <pattern-list> ... accept & report matching file
|
||||
# DENY <pattern-list> ... reject & report matching file
|
||||
# IGNORE <pattern-list> ... silently reject matching file
|
||||
#
|
||||
# A <pattern-list> is a space-separated list of shell pattern.
|
||||
# It may also be empty, in which case it has no effect. Examples:
|
||||
#
|
||||
# DENY test.blend
|
||||
# ALLOW *.xcf *.blend
|
||||
#
|
||||
# The list of pattern is checked in the same order in which it was
|
||||
# built. The first match causes a file to be accepted or rejected.
|
||||
# Further matches are not considered. Comments using the hash
|
||||
# character '#' are allowed and ignored.
|
||||
#
|
||||
# Some default rules are always added at the end. If you want to
|
||||
# bypass them, then finish your configuration with an "ALLOW *"
|
||||
# or "DENY *", and no file will ever reach the default rules.
|
||||
#
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# DENY test.xcf # throw out the test image, but ...
|
||||
# ALLOW *.xcf # ... allow all other GIMP images (the default
|
||||
# # rules would otherwise throw them out)
|
||||
#
|
||||
# ALLOW not.old # add this file, but ...
|
||||
# IGNORE *.old # throw out all other "old" files (and don't
|
||||
# # report that to the terminal)
|
||||
#
|
||||
#
|
||||
# .fg-submit configuration files are "sourced" bash scripts, the
|
||||
# keywords are simple shell functions. That means that you can
|
||||
# also use other bash commands in that file, such as "echo", or
|
||||
# write several commands on one line, separated with semicolon.
|
||||
# You can even put all special rules in your ~/.fg-submit file,
|
||||
# with rules depending on the working directory:
|
||||
#
|
||||
# case "$PWD" in
|
||||
# */bo105*) DENY *.osg; ALLOW livery.xcf ;;
|
||||
# */ufo*) DENY *.tiff ;;
|
||||
# esac
|
||||
|
||||
|
||||
|
||||
SELF=${0##*/}
|
||||
DIR=${PWD##*/}
|
||||
|
||||
if [ "$1" == "-v" ]; then
|
||||
DBG=1
|
||||
shift
|
||||
fi
|
||||
|
||||
BASE=${1:-$DIR}
|
||||
BASE=${BASE// /%20}
|
||||
|
||||
CVS=/usr/bin/cvs # avoid colorcvs wrapper from
|
||||
[ -x $CVS ] || CVS=cvs # http://www.hakubi.us/colorcvs/
|
||||
UPLOAD=$(which fg-upload 2>/dev/null)
|
||||
|
||||
CONFIG_FILE=".fg-submit"
|
||||
ARCHIVE=$BASE.tar.bz2
|
||||
DIFF=$BASE.diff
|
||||
CDIFF=$DIFF.bz2
|
||||
|
||||
# these rules are always prepended; the first letter decides if the
|
||||
# rule accepts (+), rejects (-), or ignores (!) a matching file.
|
||||
PREFIX_RULES="
|
||||
!$DIFF* !$CDIFF* !$ARCHIVE*
|
||||
!CVS/* !*/CVS/*
|
||||
"
|
||||
# these rules are always appended
|
||||
DEFAULT_RULES="
|
||||
+.cvsignore +*/.cvsignore
|
||||
-*~ -*. -*.bak -*.orig
|
||||
-*.RGB -*.RGBA -*.MDL
|
||||
-*.XCF -*.tga -*.TGA -*.bmp -*.BMP -*.PNG
|
||||
-*.blend -*.blend[0-9] -*blend[0-9][0-9] -*.blend[0-9][0-9][0-9]
|
||||
-*.gz -*.tgz -*.bz2 -*.zip -*.tar.gz* -*.tar.bz2*
|
||||
"
|
||||
POSTFIX_RULES="
|
||||
!.* !*/.*
|
||||
+*
|
||||
"
|
||||
|
||||
|
||||
|
||||
function ERROR { echo -e "\e[31;1m$*\e[m"; }
|
||||
function LOG { echo -e "\e[35m$*\e[m"; }
|
||||
function NEW { echo -e "\e[32m\t+ $*\e[m"; }
|
||||
function CHANGED { echo -e "\e[36m\t+ $*\e[m"; }
|
||||
function REJECT { echo -e "\e[31m\t- $*\e[m"; }
|
||||
function DEBUG { [ $DBG ] && echo -e "$*"; }
|
||||
|
||||
function diffstat {
|
||||
# output diff statistics, similar to the "diffstat" utility
|
||||
awk '
|
||||
function line(a, r, c, f) {
|
||||
print "\t\033[32m"a"\033[m\t\033[31m"r"\033[m\t\033[34m"c"\033[m\t"f
|
||||
}
|
||||
function dofile() {
|
||||
if (!file) {
|
||||
return
|
||||
}
|
||||
if (bin) {
|
||||
print "\t. . . . binary . . . . \033[36m"file"\033[m"
|
||||
} else {
|
||||
line(a, r, c, file)
|
||||
at += a; rt += r; ct += c
|
||||
}
|
||||
a = r = c = 0
|
||||
}
|
||||
BEGIN {
|
||||
print "\tadded---removed-changed----------------------------------------"
|
||||
a = r = c = at = rt = ct = n = bin = 0
|
||||
}
|
||||
/^Index: / { dofile(); scan = bin = 0; file = $2; n++; next }
|
||||
/^@@/ { scan = 1; next }
|
||||
/^Binary/ { if (!scan) bin = 1; next }
|
||||
/^\+/ { if (scan) a++; next }
|
||||
/^-/ { if (scan) r++; next }
|
||||
/^!/ { if (scan) c++; next }
|
||||
END {
|
||||
dofile()
|
||||
print "\t----------------------------------------total------------------"
|
||||
line(at, rt, ct, "\033[min "n" files")
|
||||
}
|
||||
' <$1
|
||||
}
|
||||
|
||||
function backup_filename {
|
||||
for ((i = 1; 1; i = i + 1)); do
|
||||
name=$1.$i
|
||||
if ! [ -a "$name" ]; then
|
||||
touch $name
|
||||
echo $name
|
||||
return
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
|
||||
# set up accept/reject rules
|
||||
function ALLOW { for i in $*; do RULES="$RULES +$i"; done }
|
||||
function DENY { for i in $*; do RULES="$RULES -$i"; done }
|
||||
function IGNORE { for i in $*; do RULES="$RULES !$i"; done }
|
||||
|
||||
|
||||
function search_config {
|
||||
file="$1/$CONFIG_FILE"
|
||||
DEBUG "checking for config file $file"
|
||||
if [ -f "$file" ]; then
|
||||
CONFIG="$file"
|
||||
return 0
|
||||
elif [ "$1" ]; then
|
||||
search_config ${1%/${1##*/}} # parent dir
|
||||
return
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
set -f
|
||||
RULES=
|
||||
if search_config "$PWD"; then
|
||||
LOG "loading config file $CONFIG"
|
||||
source "$CONFIG"
|
||||
elif [ -f ~/$CONFIG_FILE ]; then
|
||||
DEBUG "loading config file ~/$CONFIG_FILE"
|
||||
source ~/$CONFIG_FILE
|
||||
elif [ -f ~/${CONFIG_FILE}rc ]; then
|
||||
DEBUG "loading config file ~/${CONFIG}rc"
|
||||
source ~/${CONFIG_FILE}rc
|
||||
fi
|
||||
RULES="$PREFIX_RULES $RULES $DEFAULT_RULES $POSTFIX_RULES"
|
||||
set +f
|
||||
|
||||
|
||||
if [ $DBG ]; then
|
||||
DEBUG "using these rules: "
|
||||
for i in $RULES; do echo -n "$i "; done
|
||||
echo
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# create temporary dir that's automatically removed on exit
|
||||
TMP=$(mktemp -d /tmp/$SELF.$BASE.XXXXXX) || (echo "$0: can't create temporary dir"; exit 1)
|
||||
trap "rm -rf $TMP" 0 1 2 3 13 15
|
||||
|
||||
|
||||
|
||||
# move old files out of the way adding sequential suffixes
|
||||
for i in $DIFF $CDIFF $ARCHIVE; do
|
||||
[ -f $i ] && mv $i $(backup_filename $i)
|
||||
done
|
||||
|
||||
|
||||
|
||||
LOG "updating and checking for new files ..."
|
||||
$CVS -q up -dP >$TMP/up || exit 1
|
||||
|
||||
|
||||
if grep "^C " $TMP/up &>/dev/null; then
|
||||
ERROR "there are conflicts with the following files:"
|
||||
grep "^C " $TMP/up
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
|
||||
LOG "making diff ..."
|
||||
if ! $CVS -q diff -up >$DIFF; then
|
||||
LOG "diff statistics:"
|
||||
diffstat $DIFF
|
||||
echo
|
||||
|
||||
# add diff file itself
|
||||
echo $DIFF >>$TMP/files
|
||||
|
||||
# add changed binary files
|
||||
awk '
|
||||
/^Index: / { scan = 1; file = $2; next }
|
||||
/^@@/ { scan = 0; next }
|
||||
/^Binary/ { if (scan) { print file } }
|
||||
' <$DIFF >>$TMP/files
|
||||
else
|
||||
rm -f $DIFF
|
||||
fi
|
||||
|
||||
|
||||
|
||||
LOG "checking for files to submit ..."
|
||||
if [ -f $TMP/files ]; then
|
||||
cat $TMP/files|while read i; do
|
||||
CHANGED "$i"
|
||||
done
|
||||
fi
|
||||
|
||||
grep "^? " $TMP/up|while read i; do
|
||||
find ${i#? } -type f >>$TMP/check
|
||||
done
|
||||
|
||||
|
||||
|
||||
# filter files according to the pattern rules
|
||||
if [ -f $TMP/check ]; then
|
||||
for i in $(cat $TMP/check); do
|
||||
DEBUG "checking whether file '$i' matches"
|
||||
for r in $RULES; do
|
||||
DEBUG "\t\trule $r"
|
||||
class=${r:0:1}
|
||||
rule=${r:1}
|
||||
case "$i" in $rule)
|
||||
case $class in
|
||||
!) DEBUG "$i\t\t\"silently\" rejected\t\t$rule" ;;
|
||||
-) REJECT "$i\t\t$rule" ;;
|
||||
+) NEW "$i\t\t$rule" && echo "$i" >>$TMP/files ;;
|
||||
esac
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if ! [ -f $TMP/files ]; then
|
||||
LOG "no changed or new files found"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo
|
||||
numfiles=$(awk '//{n++}END{print n}' <$TMP/files)
|
||||
if [ -f $DIFF -a $numfiles == 1 ]; then
|
||||
LOG "only changed non-binary files found"
|
||||
LOG "creating compressed diff \e[1;37;40m$CDIFF\e[m\e[35m ..."
|
||||
bzip2 --keep $DIFF
|
||||
RESULT=$CDIFF
|
||||
else
|
||||
LOG "changed and/or new files found"
|
||||
LOG "creating archive \e[1;37;40m$ARCHIVE\e[m\e[35m ..."
|
||||
tar --create --bzip2 --file=$ARCHIVE --files-from $TMP/files
|
||||
RESULT=$ARCHIVE
|
||||
fi
|
||||
|
||||
|
||||
[ -x "$UPLOAD" -a -f $RESULT ] && $UPLOAD $RESULT $DIFF
|
||||
|
||||
|
||||
302
scripts/tools/freq
Executable file
302
scripts/tools/freq
Executable file
@@ -0,0 +1,302 @@
|
||||
#!/usr/bin/perl -w
|
||||
# $Id$
|
||||
# Melchior FRANZ <mfranz#aon:at> Public Domain
|
||||
#
|
||||
# Usage: $ freq [IACO:ksfo [RANGE:15]]
|
||||
#
|
||||
# Examples: $ freq
|
||||
# $ freq ksjc
|
||||
# $ freq ksjc 30
|
||||
#
|
||||
# The RANGE is in km and defines which NDB, VOR, VORTAC, ... to
|
||||
# display. Default is 15 km.
|
||||
#
|
||||
# Note that the directions given for NDB, VOR, VORTAC, ... are
|
||||
# always the heading from this radio facility to the airport!
|
||||
|
||||
use strict;
|
||||
use POSIX qw(ceil floor);
|
||||
|
||||
my $ID = shift || "KSFO";
|
||||
my $RANGE = shift || 15; # for NDB/VOR [km]
|
||||
|
||||
my $FG_ROOT = $ENV{'FG_ROOT'} || "/usr/local/share/FlightGear";
|
||||
my $APTFILE = "$FG_ROOT/Airports/apt.dat.gz" || die "airport file not found";
|
||||
my $NAVFILE = "$FG_ROOT/Navaids/nav.dat.gz" || die "nav file not found";
|
||||
|
||||
$ID = uc($ID);
|
||||
my $PI = 3.1415926535897932384626433832795029;
|
||||
my $D2R = $PI / 180;
|
||||
my $R2D = 180 / $PI;
|
||||
my $ERAD = 6378138.12;
|
||||
my %COLOR = (
|
||||
'NONE' => "\033[m",
|
||||
'DME' => "\033[34;1",
|
||||
'ILS' => "\033[33;1m",
|
||||
'TWR' => "\033[31;1m",
|
||||
'ATIS' => "\033[32;1m",
|
||||
'NDB' => "\033[36;1m",
|
||||
'VOR' => "\033[35;1m",
|
||||
);
|
||||
my $USECOLOR = 1;
|
||||
|
||||
my %FREQ;
|
||||
|
||||
my $aptdatacnt = 0;
|
||||
my $aptlat = 0;
|
||||
my $aptlon = 0;
|
||||
|
||||
open(F, "gzip -d -c $APTFILE|") or die "can't open airport file $APTFILE";
|
||||
while (<F>) {
|
||||
if (/^1\s+\S+\s+\S+\s+\S+\s+$ID\s+(.+)\s+/) {
|
||||
my $title = "$ID - $1";
|
||||
print "$title\n";
|
||||
print "=" x length($title) . "\n";
|
||||
|
||||
foreach (<F>) {
|
||||
chomp;
|
||||
last if /^\s*$/;
|
||||
|
||||
if (/^1.\s+(\S+)\s+(\S+)\s+/) {
|
||||
my ($lat, $lon) = ($1, $2);
|
||||
map { s/^(-?)0+/$1/ } ($lat, $lon);
|
||||
$aptlat += $lat;
|
||||
$aptlon += $lon;
|
||||
$aptdatacnt++;
|
||||
} elsif (/^(5\d+)\s+(\d+)\s+(.*)\s*/) {
|
||||
my ($id, $freq, $desc) = ($1, $2, $3);
|
||||
$freq =~ s/(..)$/.$1/;
|
||||
&addfreq($freq, $desc);
|
||||
}
|
||||
}
|
||||
last;
|
||||
}
|
||||
}
|
||||
close F or die "can't close airport file $APTFILE";
|
||||
|
||||
die "no data for $ID" unless $aptdatacnt;
|
||||
|
||||
# calculate mean location from all structures on the airport
|
||||
$aptlat /= $aptdatacnt;
|
||||
$aptlon /= $aptdatacnt;
|
||||
my ($aptx, $apty, $aptz) = &ll2xyz($aptlat, $aptlon);
|
||||
|
||||
|
||||
my @OM;
|
||||
my @MM;
|
||||
my @IM;
|
||||
my @NDB;
|
||||
my @VOR;
|
||||
my @DME;
|
||||
my @OTHERS;
|
||||
|
||||
open(F, "gzip -d -c $NAVFILE|") or die "can't open airport file $NAVFILE";
|
||||
while (<F>) {
|
||||
chomp;
|
||||
if (/^2\s/) { # NDB
|
||||
my @l = split /\s+/, $_, 9;
|
||||
map { s/^(-?)0+/$1/ } @l[1,2];
|
||||
my $dist = &coord_dist_sq(&ll2xyz($l[1], $l[2]), $aptx, $apty, $aptz);
|
||||
push @NDB, [$dist, @l];
|
||||
|
||||
} elsif (/^3\s/) { # VOR/VOR-DME/DME/VORTAC/TACAN
|
||||
my @l = split /\s+/, $_, 9;
|
||||
map { s/^(-?)0+/$1/ } @l[1,2];
|
||||
my $dist = &coord_dist_sq(&ll2xyz($l[1], $l[2]), $aptx, $apty, $aptz);
|
||||
if ($l[8] =~ /\b(VOR|VOR-DME)$/) {
|
||||
push @VOR, [$dist, @l];
|
||||
} elsif ($l[8] =~ /\bDME\b/) {
|
||||
push @DME, [$dist, @l];
|
||||
} else {
|
||||
push @OTHERS, [$dist, @l];
|
||||
}
|
||||
|
||||
} elsif (/^(4|5)\s/) { # LLZ
|
||||
my @l = split /\s+/, $_, 11;
|
||||
next unless $l[8] eq $ID;
|
||||
$l[4] =~ s/(..)$/.$1/;
|
||||
&addfreq($l[4], "LLZ " . $l[9]);
|
||||
|
||||
} elsif (/^6\s/) { # GS
|
||||
my @l = split /\s+/, $_, 11;
|
||||
next unless $l[8] eq $ID;
|
||||
$l[4] =~ s/(..)$/.$1/;
|
||||
&addfreq($l[4], "GS " . $l[9]);
|
||||
|
||||
} elsif (/^7\s/) { # OM
|
||||
my @l = split /\s+/, $_, 11;
|
||||
next unless $l[8] eq $ID;
|
||||
push @OM, $l[9];
|
||||
|
||||
} elsif (/^8\s/) { # MM
|
||||
my @l = split /\s+/, $_, 11;
|
||||
next unless $l[8] eq $ID;
|
||||
push @MM, $l[9];
|
||||
|
||||
} elsif (/^9\s/) { # IM
|
||||
my @l = split /\s+/, $_, 11;
|
||||
next unless $l[8] eq $ID;
|
||||
push @IM, $l[9];
|
||||
|
||||
} elsif (/^12\s/) { # DME (ILS)
|
||||
my @l = split /\s+/, $_, 11;
|
||||
next unless $l[8] eq $ID;
|
||||
$l[4] =~ s/(..)$/.$1/;
|
||||
&addfreq($l[4], "DME " . $l[9]);
|
||||
|
||||
}
|
||||
}
|
||||
close F or die "can't close airport file $NAVFILE";
|
||||
|
||||
|
||||
foreach my $freq (sort { $a <=> $b } keys %FREQ) {
|
||||
my %h;
|
||||
map { $h{$_} = 1 } @{$FREQ{$freq}};
|
||||
my @uniq = keys %h;
|
||||
|
||||
my @desc;
|
||||
my %rwy;
|
||||
foreach my $d (@uniq) {
|
||||
if ($d =~ /(\S*)\s*(\d\d[LRC]?)\s*(\S*)/) {
|
||||
push @{$rwy{$2}}, ($1 . $3);
|
||||
} else {
|
||||
push @desc, $d;
|
||||
}
|
||||
}
|
||||
foreach my $r (keys %rwy) {
|
||||
push @desc, ((join "/", sort @{$rwy{$r}}) . " $r");
|
||||
}
|
||||
|
||||
my $s;
|
||||
my $k = join ", ", @desc;
|
||||
if ($k =~ /\bTWR\b/) {
|
||||
$s = $COLOR{'TWR'};
|
||||
} elsif ($k =~ /\bATIS\b/) {
|
||||
$s = $COLOR{'ATIS'};
|
||||
} elsif ($k =~ /\b(GZ|LLZ)\b/) {
|
||||
$s = $COLOR{'ILS'};
|
||||
}
|
||||
$s .= sprintf "%-7s %s\033[m\n", $freq, join ", ", $k;
|
||||
print $s;
|
||||
}
|
||||
|
||||
|
||||
&printfreq(0, $COLOR{'NDB'}, @NDB);
|
||||
&printfreq(1, $COLOR{'VOR'}, @VOR);
|
||||
&printfreq(1, $COLOR{'DME'}, @DME);
|
||||
&printfreq(1, $COLOR{'NONE'}, @OTHERS);
|
||||
|
||||
print " OM " . (join ", ", sort @OM) . "\n" if @OM;
|
||||
print " MM " . (join ", ", sort @MM) . "\n" if @MM;
|
||||
print " IM " . (join ", ", sort @IM) . "\n" if @IM;
|
||||
|
||||
exit 0;
|
||||
|
||||
|
||||
|
||||
sub printfreq($$$)
|
||||
{
|
||||
my $divfreq = shift; # divide frequency by 100?
|
||||
my $color = shift;
|
||||
foreach (sort { @{$a}[0] <=> @{$b}[0] } @_) {
|
||||
my @l = @{$_};
|
||||
my $dist = &distance($l[0]);
|
||||
my $dir = &llll2dir($l[2], $l[3], $aptlat, $aptlon);
|
||||
my $freq = $l[5];
|
||||
$freq =~ s/(..)$/.$1/ if $divfreq;
|
||||
printf "$color%-7s %s (\"%s\")\t-->\t%s km/%s nm @ %s (%s)$COLOR{'NONE'}\n",
|
||||
$freq, $l[9], $l[8],
|
||||
&round($dist, 0.1), # km
|
||||
&round($dist / 1.852, 0.1), # nm
|
||||
int $dir, &symdir($dir);
|
||||
next if $l[9] =~ /\b$ID\b/;
|
||||
last if $dist > $RANGE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub addfreq($$)
|
||||
{
|
||||
my ($freq, $desc) = @_;
|
||||
push @{$FREQ{$freq}}, $desc;
|
||||
}
|
||||
|
||||
|
||||
sub distance($) # km
|
||||
{
|
||||
my $t = shift;
|
||||
return $ERAD * sqrt($t) / 1000;
|
||||
}
|
||||
|
||||
|
||||
sub round($)
|
||||
{
|
||||
my $i = shift;
|
||||
my $m = (shift or 1);
|
||||
$i /= $m;
|
||||
$i = $i - &floor($i) >= 0.5 ? &ceil($i) : &floor($i);
|
||||
$i *= $m;
|
||||
return $i;
|
||||
}
|
||||
|
||||
|
||||
sub llll2dir($$$$)
|
||||
{
|
||||
my $latA = (shift) * $D2R;
|
||||
my $lonA = (shift) * $D2R;
|
||||
my $latB = (shift) * $D2R;
|
||||
my $lonB = (shift) * $D2R;
|
||||
my $xdist = sin($lonB - $lonA) * $ERAD * cos(($latA + $latB) / 2);
|
||||
my $ydist = sin($latB - $latA) * $ERAD;
|
||||
my $dir = atan2($xdist, $ydist) * $R2D;
|
||||
$dir += 360 if $dir < 0;
|
||||
return $dir;
|
||||
}
|
||||
|
||||
|
||||
sub ll2xyz($$)
|
||||
{
|
||||
my $lat = (shift) * $D2R;
|
||||
my $lon = (shift) * $D2R;
|
||||
my $cosphi = cos $lat;
|
||||
my $di = $cosphi * cos $lon;
|
||||
my $dj = $cosphi * sin $lon;
|
||||
my $dk = sin $lat;
|
||||
return ($di, $dj, $dk);
|
||||
}
|
||||
|
||||
|
||||
sub xyz2ll($$$)
|
||||
{
|
||||
my ($di, $dj, $dk) = @_;
|
||||
my $aux = $di * $di + $dj * $dj;
|
||||
my $lat = atan2($dk, sqrt $aux) * $R2D;
|
||||
my $lon = atan2($dj, $di) * $R2D;
|
||||
return ($lat, $lon);
|
||||
}
|
||||
|
||||
|
||||
sub coord_dist_sq($$$$$$)
|
||||
{
|
||||
my ($xa, $ya, $za, $xb, $yb, $zb) = @_;
|
||||
my $x = $xb - $xa;
|
||||
my $y = $yb - $ya;
|
||||
my $z = $zb - $za;
|
||||
return $x * $x + $y * $y + $z * $z;
|
||||
}
|
||||
|
||||
|
||||
sub symdir($)
|
||||
{
|
||||
my $dir = shift;
|
||||
my @names = ("N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
|
||||
"S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW");
|
||||
my $nnames = scalar @names;
|
||||
my $idx = int($nnames * (($dir / 360) + (0.5 / $nnames)));
|
||||
if ($idx >= $nnames) {
|
||||
$idx = 0;
|
||||
}
|
||||
return $names[$idx];
|
||||
}
|
||||
|
||||
|
||||
289
scripts/tools/lsprop
Executable file
289
scripts/tools/lsprop
Executable file
@@ -0,0 +1,289 @@
|
||||
#!/usr/bin/python
|
||||
import glob, os, sys, string, xml.sax, getopt
|
||||
|
||||
|
||||
__doc__ = """\
|
||||
List properties defined in FlightGear's <PropertyList> XML files.
|
||||
|
||||
Usage:
|
||||
lsprop [-v] [-p] [-i|-I] [-f <format>] [<list-of-xml-files>]
|
||||
lsprop -h
|
||||
|
||||
Options:
|
||||
-h, --help print this help screen
|
||||
-v, --verbose increase verbosity
|
||||
-i, --all-indices also show null indices in properties
|
||||
-I, --no-indices don't show any indices in properties
|
||||
-p, --raw-paths don't use symbols "$FG_ROOT" and "$FG_HOME" as path prefix
|
||||
-f, --format set output format (default: --format="%f +%l: %p = '%v'")
|
||||
|
||||
Format:
|
||||
%f file path
|
||||
%l line number
|
||||
%c column number
|
||||
%p property path
|
||||
%t property type
|
||||
%V raw value (unescaped)
|
||||
%v cooked value (carriage return, non printable chars etc. escaped)
|
||||
%q like %v, but single quotes escaped to \\'
|
||||
%Q like %v, but double quotes escaped to \\"
|
||||
%% percent sign
|
||||
|
||||
Environment:
|
||||
FG_ROOT
|
||||
FG_HOME
|
||||
LSPROP_FORMAT overrides default format
|
||||
|
||||
Arguments:
|
||||
If no file arguments are specified, then the following files are assumed:
|
||||
$FG_ROOT/preferences.xml
|
||||
$FG_ROOT/Aircraft/*/*-set.xml
|
||||
|
||||
Current settings:\
|
||||
"""
|
||||
|
||||
|
||||
class config:
|
||||
root = "/usr/local/share/FlightGear"
|
||||
home = os.environ["HOME"] + "/.fgfs"
|
||||
raw_paths = 0
|
||||
format = "%f +%l: %p = '%v'"
|
||||
verbose = 1
|
||||
indices = 1 # 0: no indices; 1: only indices != [0]; 2: all indices
|
||||
|
||||
|
||||
def errmsg(msg, color = "31;1"):
|
||||
if os.isatty(2):
|
||||
print >>sys.stderr, "\033[%sm%s\033[m" % (color, msg)
|
||||
else:
|
||||
print >>sys.stderr, msg
|
||||
|
||||
|
||||
def cook_path(path, force = 0):
|
||||
path = os.path.normpath(os.path.abspath(path))
|
||||
if config.raw_paths and not force:
|
||||
return path
|
||||
if path.startswith(config.root):
|
||||
path = path.replace(config.root, "$FG_ROOT", 1)
|
||||
elif path.startswith(config.home):
|
||||
path = path.replace(config.home, "$FG_HOME", 1)
|
||||
return path
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Abort(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class XMLError(Exception):
|
||||
def __init__(self, locator, msg):
|
||||
msg = "%s in %s +%d:%d" \
|
||||
% (msg.replace("\n", "\\n"), cook_path(locator.getSystemId()), \
|
||||
locator.getLineNumber(), locator.getColumnNumber())
|
||||
raise Error(msg)
|
||||
|
||||
|
||||
class parse_xml_file(xml.sax.handler.ContentHandler):
|
||||
def __init__(self, path, nesting = 0, stack = None):
|
||||
self.level = 0
|
||||
self.path = path
|
||||
self.nesting = nesting
|
||||
self.type = None
|
||||
if stack:
|
||||
self.stack = stack
|
||||
else:
|
||||
self.stack = [[None, None, {}, []]] # name, index, indices, data
|
||||
|
||||
self.pretty_path = cook_path(path)
|
||||
|
||||
if config.verbose > 1:
|
||||
errmsg("FILE %s (%d)" % (path, nesting), "35")
|
||||
if not os.path.exists(path):
|
||||
raise Error("file doesn't exist: " + self.pretty_path)
|
||||
|
||||
try:
|
||||
xml.sax.parse(path, self, self)
|
||||
except ValueError:
|
||||
pass # FIXME hack arount DTD error
|
||||
|
||||
def startElement(self, name, attrs):
|
||||
self.level += 1
|
||||
if self.level == 1:
|
||||
if name != "PropertyList":
|
||||
raise XMLError(self.locator, "XML file isn't a <PropertyList>")
|
||||
else:
|
||||
index = 0
|
||||
if attrs.has_key("n"):
|
||||
index = int(attrs["n"])
|
||||
elif name in self.stack[-1][2]:
|
||||
index = self.stack[-1][2][name] + 1
|
||||
self.stack[-1][2][name] = index
|
||||
|
||||
self.type = "unspecified"
|
||||
if attrs.has_key("type"):
|
||||
self.type = attrs["type"]
|
||||
|
||||
if attrs.has_key("include"):
|
||||
path = os.path.dirname(os.path.abspath(self.path)) + "/" + attrs["include"]
|
||||
if attrs.has_key("omit-node") and attrs["omit-node"] == "y" or self.level == 1:
|
||||
self.stack.append([None, None, self.stack[-1][2], []])
|
||||
else:
|
||||
self.stack.append([name, index, {}, []])
|
||||
parse_xml_file(path, self.nesting + 1, self.stack)
|
||||
elif self.level > 1:
|
||||
self.stack.append([name, index, {}, []])
|
||||
|
||||
def endElement(self, name):
|
||||
value = string.join(self.stack[-1][3], '')
|
||||
if not len(self.stack[-1][2]) and self.level > 1:
|
||||
path = self.pathname()
|
||||
if path:
|
||||
cooked_value = self.escape(value.encode("iso-8859-15", "backslashreplace"))
|
||||
print config.cooked_format % {
|
||||
"f": self.pretty_path,
|
||||
"l": self.locator.getLineNumber(),
|
||||
"c": self.locator.getColumnNumber(),
|
||||
"p": path,
|
||||
"t": self.type,
|
||||
"V": value,
|
||||
"v": cooked_value,
|
||||
"q": cooked_value.replace("'", "\\'"),
|
||||
'Q': cooked_value.replace('"', '\\"'),
|
||||
}
|
||||
|
||||
elif len(string.strip(value)):
|
||||
raise XMLError(self.locator, "garbage found '%s'" % string.strip(value))
|
||||
|
||||
self.level -= 1
|
||||
if self.level:
|
||||
self.stack.pop()
|
||||
|
||||
def characters(self, data):
|
||||
self.stack[-1][3].append(data)
|
||||
|
||||
def setDocumentLocator(self, locator):
|
||||
self.locator = locator
|
||||
|
||||
def pathname(self):
|
||||
path = ""
|
||||
for e in self.stack[1:]:
|
||||
if e[0] == None: # omit-node
|
||||
continue
|
||||
path += "/" + e[0]
|
||||
if e[1] and config.indices == 1 or config.indices == 2:
|
||||
path += "[%d]" % e[1]
|
||||
return path
|
||||
|
||||
def escape(self, string):
|
||||
s = ""
|
||||
for c in string:
|
||||
if c == '\n':
|
||||
s += '\\n'
|
||||
elif c == '\r':
|
||||
s += '\\r'
|
||||
elif c == '\v':
|
||||
s += '\\v'
|
||||
elif c == '\\':
|
||||
s += '\\\\'
|
||||
elif not c.isalnum() and " \t!@#$%^&*()_+|~-=\`[]{};':\",./<>?".find(c) < 0:
|
||||
s += "\\x%02x" % ord(c)
|
||||
else:
|
||||
s += c
|
||||
return s
|
||||
|
||||
def warning(self, exception):
|
||||
raise XMLError(self.locator, "WARNING: " + str(exception))
|
||||
|
||||
def error(self, exception):
|
||||
raise XMLError(self.locator, "ERROR: " + str(exception))
|
||||
|
||||
def fatalError(self, exception):
|
||||
raise XMLError(self.locator, "FATAL: " + str(exception))
|
||||
|
||||
|
||||
def main():
|
||||
if 'FG_ROOT' in os.environ:
|
||||
config.root = os.environ['FG_ROOT'].lstrip().rstrip("/\\\t ")
|
||||
if 'FG_HOME' in os.environ:
|
||||
config.home = os.environ['FG_HOME'].lstrip().rstrip("/\\\t ")
|
||||
if 'LSPROP_FORMAT' in os.environ:
|
||||
config.format = os.environ['LSPROP_FORMAT']
|
||||
|
||||
# options
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], \
|
||||
"hviIpf:", \
|
||||
["help", "verbose", "all-indices", "no-indices", "raw-paths", "format="])
|
||||
except getopt.GetoptError, msg:
|
||||
errmsg("Error: %s" % msg)
|
||||
return -1
|
||||
|
||||
for o, a in opts:
|
||||
if o in ("-h", "--help"):
|
||||
print __doc__
|
||||
print '\t--format="%s"' % config.format.replace('"', '\\"')
|
||||
return 0
|
||||
if o in ("-v", "--verbose"):
|
||||
config.verbose += 1
|
||||
if o in ("-i", "--all-indices"):
|
||||
config.indices = 2
|
||||
if o in ("-I", "--no-indices"):
|
||||
config.indices = 0
|
||||
if o in ("-p", "--raw-paths"):
|
||||
config.raw_paths = 1
|
||||
if o in ("-f", "--format"):
|
||||
config.format = a
|
||||
|
||||
# format
|
||||
f = config.format
|
||||
f = f.replace("\\e", "\x1b")
|
||||
f = f.replace("\\033", "\x1b")
|
||||
f = f.replace("\\x1b", "\x1b")
|
||||
f = f.replace("%%", "\x01\x01")
|
||||
f = f.replace("%f", "\x01(f)s")
|
||||
f = f.replace("%l", "\x01(l)d")
|
||||
f = f.replace("%c", "\x01(c)d")
|
||||
f = f.replace("%p", "\x01(p)s")
|
||||
f = f.replace("%t", "\x01(t)s")
|
||||
f = f.replace("%V", "\x01(V)s")
|
||||
f = f.replace("%v", "\x01(v)s")
|
||||
f = f.replace("%q", "\x01(q)s")
|
||||
f = f.replace('%Q', '\x01(Q)s')
|
||||
f = f.replace("%", "%%")
|
||||
f = f.replace("\x01", "%")
|
||||
config.cooked_format = f
|
||||
|
||||
if config.verbose > 2:
|
||||
print >>sys.stderr, "internal format = [%s]" % config.cooked_format
|
||||
|
||||
# arguments
|
||||
if not len(args):
|
||||
args = [config.root + "/preferences.xml"]
|
||||
if not os.path.exists(args[0]):
|
||||
errmsg("Error: environment variable FG_ROOT not set or set wrongly?")
|
||||
return -1
|
||||
for f in glob.glob(config.root + '/Aircraft/*/*-set.xml'):
|
||||
args.append(f)
|
||||
|
||||
for arg in args:
|
||||
try:
|
||||
parse_xml_file(arg)
|
||||
except Abort, e:
|
||||
errmsg("Abort: " + e.args[0])
|
||||
return -1
|
||||
except Error, e:
|
||||
errmsg("Error: " + e.args[0])
|
||||
except IOError, (errno, msg):
|
||||
errmsg("Error: " + msg)
|
||||
return errno
|
||||
except KeyboardInterrupt:
|
||||
print >>sys.stderr, "\033[m"
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
||||
Reference in New Issue
Block a user