Add some example remote controll scripts
This commit is contained in:
parent
10fc1ea537
commit
b8ac668719
6 changed files with 563 additions and 0 deletions
2
scripts/example/.cvsignore
Normal file
2
scripts/example/.cvsignore
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
1
scripts/example/Makefile.am
Normal file
1
scripts/example/Makefile.am
Normal file
|
@ -0,0 +1 @@
|
||||||
|
EXTRA_DIST = fgfsclient.c fgfsclient.cxx fgfsscript remote.html
|
151
scripts/example/fgfsclient.c
Normal file
151
scripts/example/fgfsclient.c
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
/* $Id$ */
|
||||||
|
/* gcc -O2 -g -pedantic -Wall fgfsclient.c -o fgfsclient */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define DFLTHOST "localhost"
|
||||||
|
#define DFLTPORT 5501
|
||||||
|
#define MAXMSG 256
|
||||||
|
#define fgfsclose close
|
||||||
|
|
||||||
|
|
||||||
|
void init_sockaddr(struct sockaddr_in *name, const char *hostname, unsigned port);
|
||||||
|
int fgfswrite(int sock, char *msg, ...);
|
||||||
|
const char *fgfsread(int sock, int wait);
|
||||||
|
void fgfsflush(int sock);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int fgfswrite(int sock, char *msg, ...)
|
||||||
|
{
|
||||||
|
va_list va;
|
||||||
|
ssize_t len;
|
||||||
|
char buf[MAXMSG];
|
||||||
|
|
||||||
|
va_start(va, msg);
|
||||||
|
vsprintf(buf, msg, va);
|
||||||
|
va_end(va);
|
||||||
|
printf("SEND: \t<%s>\n", buf);
|
||||||
|
strcat(buf, "\015\012");
|
||||||
|
|
||||||
|
len = write(sock, buf, strlen(buf));
|
||||||
|
if (len < 0) {
|
||||||
|
perror("fgfswrite");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const char *fgfsread(int sock, int timeout)
|
||||||
|
{
|
||||||
|
static char buf[MAXMSG];
|
||||||
|
char *p;
|
||||||
|
fd_set ready;
|
||||||
|
struct timeval tv;
|
||||||
|
ssize_t len;
|
||||||
|
|
||||||
|
FD_ZERO(&ready);
|
||||||
|
FD_SET(sock, &ready);
|
||||||
|
tv.tv_sec = timeout;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
if (!select(32, &ready, 0, 0, &tv))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
len = read(sock, buf, MAXMSG - 1);
|
||||||
|
if (len < 0) {
|
||||||
|
perror("fgfsread");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (len == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (p = &buf[len - 1]; p >= buf; p--)
|
||||||
|
if (*p != '\015' && *p != '\012')
|
||||||
|
break;
|
||||||
|
*++p = '\0';
|
||||||
|
return strlen(buf) ? buf : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void fgfsflush(int sock)
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
while ((p = fgfsread(sock, 0)) != NULL) {
|
||||||
|
printf("IGNORE: \t<%s>\n", p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int fgfsconnect(const char *hostname, const int port)
|
||||||
|
{
|
||||||
|
struct sockaddr_in serv_addr;
|
||||||
|
struct hostent *hostinfo;
|
||||||
|
int sock;
|
||||||
|
|
||||||
|
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (sock < 0) {
|
||||||
|
perror("fgfsconnect/socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hostinfo = gethostbyname(hostname);
|
||||||
|
if (hostinfo == NULL) {
|
||||||
|
fprintf(stderr, "fgfsconnect: unknown host: \"%s\"\n", hostname);
|
||||||
|
close(sock);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
serv_addr.sin_port = htons(port);
|
||||||
|
serv_addr.sin_addr = *(struct in_addr *)hostinfo->h_addr;
|
||||||
|
|
||||||
|
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
|
||||||
|
perror("fgfsconnect/connect");
|
||||||
|
close(sock);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int sock;
|
||||||
|
unsigned port;
|
||||||
|
const char *hostname, *p;
|
||||||
|
|
||||||
|
hostname = argc > 1 ? argv[1] : DFLTHOST;
|
||||||
|
port = argc > 2 ? atoi(argv[2]) : DFLTPORT;
|
||||||
|
|
||||||
|
sock = fgfsconnect(hostname, port);
|
||||||
|
if (sock < 0)
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
|
||||||
|
fgfswrite(sock, "data");
|
||||||
|
fgfswrite(sock, "set /controls/engines/engine[%d]/throttle %d", 0, 1);
|
||||||
|
fgfswrite(sock, "get /sim/aircraft");
|
||||||
|
p = fgfsread(sock, 3);
|
||||||
|
if (p != NULL)
|
||||||
|
printf("READ: \t<%s>\n", p);
|
||||||
|
fgfswrite(sock, "quit");
|
||||||
|
fgfsclose(sock);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
181
scripts/example/fgfsclient.cxx
Normal file
181
scripts/example/fgfsclient.cxx
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
// $Id$
|
||||||
|
// g++ -O2 -g -pedantic -Wall fgfsclient.cxx -o fgfsclient -lstdc++
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
const int maxlen = 256;
|
||||||
|
|
||||||
|
|
||||||
|
class FGFSSocket {
|
||||||
|
int sock;
|
||||||
|
bool connected;
|
||||||
|
unsigned timeout;
|
||||||
|
public:
|
||||||
|
FGFSSocket(const char *name, const unsigned port);
|
||||||
|
~FGFSSocket() { close(); };
|
||||||
|
|
||||||
|
int write(const char *msg, ...);
|
||||||
|
const char *read(void);
|
||||||
|
inline void flush(void);
|
||||||
|
void settimeout(unsigned t) { timeout = t; };
|
||||||
|
private:
|
||||||
|
int close(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FGFSSocket::FGFSSocket(const char *hostname = "localhost", const unsigned port = 5501)
|
||||||
|
:
|
||||||
|
sock(-1),
|
||||||
|
connected(false),
|
||||||
|
timeout(1)
|
||||||
|
{
|
||||||
|
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (sock < 0)
|
||||||
|
throw("FGFSSocket/socket");
|
||||||
|
|
||||||
|
struct hostent *hostinfo;
|
||||||
|
hostinfo = gethostbyname(hostname);
|
||||||
|
if (!hostinfo) {
|
||||||
|
close();
|
||||||
|
throw("FGFSSocket/gethostbyname: unknown host");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in serv_addr;
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
serv_addr.sin_port = htons(port);
|
||||||
|
serv_addr.sin_addr = *(struct in_addr *)hostinfo->h_addr;
|
||||||
|
|
||||||
|
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
|
||||||
|
close();
|
||||||
|
throw("FGFSSocket/connect");
|
||||||
|
}
|
||||||
|
connected = true;
|
||||||
|
try {
|
||||||
|
write("data");
|
||||||
|
} catch (...) {
|
||||||
|
close();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int FGFSSocket::close(void)
|
||||||
|
{
|
||||||
|
if (connected)
|
||||||
|
write("quit");
|
||||||
|
if (sock < 0)
|
||||||
|
return 0;
|
||||||
|
int ret = ::close(sock);
|
||||||
|
sock = -1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int FGFSSocket::write(const char *msg, ...)
|
||||||
|
{
|
||||||
|
va_list va;
|
||||||
|
ssize_t len;
|
||||||
|
char buf[maxlen];
|
||||||
|
fd_set fd;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
FD_ZERO(&fd);
|
||||||
|
FD_SET(sock, &fd);
|
||||||
|
tv.tv_sec = timeout;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
if (!select(FD_SETSIZE, 0, &fd, 0, &tv))
|
||||||
|
throw("FGFSSocket::write/select: timeout exceeded");
|
||||||
|
|
||||||
|
va_start(va, msg);
|
||||||
|
vsprintf(buf, msg, va);
|
||||||
|
va_end(va);
|
||||||
|
std::cout << "SEND: " << buf << std::endl;
|
||||||
|
strcat(buf, "\015\012");
|
||||||
|
|
||||||
|
len = ::write(sock, buf, strlen(buf));
|
||||||
|
if (len < 0)
|
||||||
|
throw("FGFSSocket::write");
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char *FGFSSocket::read(void)
|
||||||
|
{
|
||||||
|
static char buf[maxlen];
|
||||||
|
char *p;
|
||||||
|
fd_set fd;
|
||||||
|
struct timeval tv;
|
||||||
|
ssize_t len;
|
||||||
|
|
||||||
|
FD_ZERO(&fd);
|
||||||
|
FD_SET(sock, &fd);
|
||||||
|
tv.tv_sec = timeout;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
if (!select(FD_SETSIZE, &fd, 0, 0, &tv)) {
|
||||||
|
if (timeout == 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
throw("FGFSSocket::read/select: timeout exceeded");
|
||||||
|
}
|
||||||
|
|
||||||
|
len = ::read(sock, buf, maxlen - 1);
|
||||||
|
if (len < 0)
|
||||||
|
throw("FGFSSocket::read/read");
|
||||||
|
if (len == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (p = &buf[len - 1]; p >= buf; p--)
|
||||||
|
if (*p != '\015' && *p != '\012')
|
||||||
|
break;
|
||||||
|
*++p = '\0';
|
||||||
|
return strlen(buf) ? buf : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void FGFSSocket::flush(void)
|
||||||
|
{
|
||||||
|
int i = timeout;
|
||||||
|
timeout = 0;
|
||||||
|
while (read())
|
||||||
|
;
|
||||||
|
timeout = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(const int argc, const char *argv[])
|
||||||
|
try {
|
||||||
|
const char *hostname = argc > 1 ? argv[1] : "localhost";
|
||||||
|
int port = argc > 2 ? atoi(argv[2]) : 5501;
|
||||||
|
|
||||||
|
FGFSSocket f(hostname, port);
|
||||||
|
f.flush();
|
||||||
|
f.write("set /controls/engines/engine[%d]/throttle %lg", 0, 1.0);
|
||||||
|
f.write("set /controls/engines/engine[%d]/throttle %lg", 1, 1.0);
|
||||||
|
f.write("get /sim/aircraft");
|
||||||
|
const char *p = f.read();
|
||||||
|
if (p)
|
||||||
|
std::cout << "RECV: " << p << std::endl;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
} catch (const char s[]) {
|
||||||
|
std::cerr << "Error: " << s << ": " << strerror(errno) << std::endl;
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
std::cerr << "Error: unknown exception" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// vim:cindent
|
95
scripts/example/fgfsscript
Normal file
95
scripts/example/fgfsscript
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#!/usr/bin/perl -w
|
||||||
|
# USAGE: fgfsscript [host [port]]
|
||||||
|
# Melchior FRANZ, a8603365@unet.univie.ac.at
|
||||||
|
# $Id$
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use IO::Socket;
|
||||||
|
|
||||||
|
my $host = (shift || 'localhost');
|
||||||
|
my $port = (shift || 5501);
|
||||||
|
my ($fgfs, $i);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# main()
|
||||||
|
{
|
||||||
|
chdir;
|
||||||
|
$fgfs = &connect($host, $port, 120) || die " can't open socket\n";
|
||||||
|
&send("data");
|
||||||
|
|
||||||
|
# wait for random altitude (0--3000 ft.) to be reached
|
||||||
|
my $alt = int(rand(3000));
|
||||||
|
print "disaster begins at $alt ft. AGL\n";
|
||||||
|
while (1) {
|
||||||
|
sleep(1);
|
||||||
|
$i = &get("/position/altitude-agl-ft");
|
||||||
|
print "\r" . int($i) . " ft.";
|
||||||
|
print "\n" and last if $i > $alt;
|
||||||
|
}
|
||||||
|
|
||||||
|
print "start fuel dumping :-)\n";
|
||||||
|
for ($i = 0; $i < 4; $i++) {
|
||||||
|
sleep(rand(60));
|
||||||
|
&set("/consumables/fuel/tank[$i]/level-gal_us", 0);
|
||||||
|
print "tank $i empty\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
&send("quit");
|
||||||
|
close $fgfs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub get()
|
||||||
|
{
|
||||||
|
&send("get " . shift);
|
||||||
|
eof $fgfs and die "\nconnection closed by host";
|
||||||
|
$_ = <$fgfs>;
|
||||||
|
s/\015?\012$//;
|
||||||
|
/^-ERR (.*)/ and die "\nfgfs error: $1\n";
|
||||||
|
return $_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub set()
|
||||||
|
{
|
||||||
|
my $prop = shift;
|
||||||
|
my $value = shift;
|
||||||
|
&send("set $prop $value");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub send()
|
||||||
|
{
|
||||||
|
print $fgfs shift, "\015\012";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub connect()
|
||||||
|
{
|
||||||
|
my $host = shift;
|
||||||
|
my $port = shift;
|
||||||
|
my $timeout = (shift || 120);
|
||||||
|
my $socket;
|
||||||
|
STDOUT->autoflush(1);
|
||||||
|
print "connect ";
|
||||||
|
while ($timeout--) {
|
||||||
|
if ($socket = IO::Socket::INET->new(
|
||||||
|
Proto => 'tcp',
|
||||||
|
PeerAddr => $host,
|
||||||
|
PeerPort => $port)) {
|
||||||
|
print ".. done.\n";
|
||||||
|
$socket->autoflush(1);
|
||||||
|
sleep 1;
|
||||||
|
return $socket;
|
||||||
|
}
|
||||||
|
print ".";
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# vi:ts=8:sw=8:noet:nowrap:cindent
|
133
scripts/example/remote.html
Normal file
133
scripts/example/remote.html
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||||
|
<title>FlightGear: Remote control</title>
|
||||||
|
<meta name="Author" content="Melchior FRANZ">
|
||||||
|
<meta name="KeyWords" content="flightgear,fgfs,flightsimulator,simulator,remote,control,perl,c,c++">
|
||||||
|
<link rel="StyleSheet" type="text/css" href="fgfs.css">
|
||||||
|
<link rel="SHORTCUT ICON" href="mf.ico">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>FlightGear: Remote control</h1>
|
||||||
|
|
||||||
|
|
||||||
|
<p>FlightGear has several interfaces that provide access to internal
|
||||||
|
parameters. The HTTP interface is best suited for human interaction
|
||||||
|
via a web browser. The telnet interface is the ideal choice for
|
||||||
|
remotely controlling FlightGear by means of external programs.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>To activate FlightGear's telnet server capabilities, call it with
|
||||||
|
a <em>--props</em> specifiaction:
|
||||||
|
|
||||||
|
<blockquote>
|
||||||
|
<pre>
|
||||||
|
$ fgfs --props=socket,bi,5,localhost,5501,tcp
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<table>
|
||||||
|
<tr><td><strong>socket:</strong></td><td>FlightGear protocol</td></tr>
|
||||||
|
<tr><td><strong>bi:</strong></td><td>bidirectional</td></tr>
|
||||||
|
<tr><td><strong>5:</strong></td><td>polling frequency in Hertz</td></tr>
|
||||||
|
<tr><td><strong>localhost: </strong></td><td>server name or IP-address</td></tr>
|
||||||
|
<tr><td><strong>5501: </strong></td><td>server port</td></tr>
|
||||||
|
<tr><td><strong>tcp:</strong></td><td>internet protocol type</td></tr>
|
||||||
|
</table>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
In newer versions of FlightGear just type:
|
||||||
|
<blockquote>
|
||||||
|
<pre>
|
||||||
|
$ fgfs --telnet=5501
|
||||||
|
</pre>
|
||||||
|
</blockquote>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>To learn more about the supported commands, connect to FlightGear
|
||||||
|
with a telnet program and type in "help<RETURN>". This is what you'll get:<p>
|
||||||
|
|
||||||
|
<blockquote>
|
||||||
|
<pre>
|
||||||
|
$ telnet localhost 5501
|
||||||
|
Trying ::1...
|
||||||
|
Trying 127.0.0.1...
|
||||||
|
Connected to localhost.
|
||||||
|
Escape character is '^]'.
|
||||||
|
help
|
||||||
|
|
||||||
|
Valid commands are:
|
||||||
|
|
||||||
|
help show help message
|
||||||
|
ls [<dir>] list directory
|
||||||
|
dump dump current state (in xml)
|
||||||
|
cd <dir> cd to a directory, '..' to move back
|
||||||
|
pwd display your current path
|
||||||
|
get <var> show the value of a parameter
|
||||||
|
show <var> synonym for get
|
||||||
|
set <var> <val> set <var> to a new <val>
|
||||||
|
data switch to raw data mode
|
||||||
|
prompt switch to interactive mode (default)
|
||||||
|
quit terminate connection
|
||||||
|
|
||||||
|
/>
|
||||||
|
</pre>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<p>Now you can browse in the property system like in a Linux file
|
||||||
|
system with <em>cd, ls, pwd</em>.</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Here you can <a href="fgfsscript">download a sample script</a>
|
||||||
|
written in Perl, that shows how to access and manipulate FlightGear's
|
||||||
|
internal parameters. It can be started before FlightGear (in which
|
||||||
|
case it tries up to 2 minutes to connect) or afterwards. Then
|
||||||
|
it picks a random AGL altitude and checks every 5 seconds
|
||||||
|
if the aircraft has already climbed at this height. Now it starts
|
||||||
|
to empty all four tanks, one after the other, until the engines
|
||||||
|
stop working. Try to find a place where you can land safely. :-)</p>
|
||||||
|
|
||||||
|
<blockquote>
|
||||||
|
<pre>
|
||||||
|
$ fgfsscript&
|
||||||
|
$ fgfs --props=socket,bi,5,localhost,5501,tcp
|
||||||
|
</pre>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<p>The script defaults to <em>localhost</em> and <em>port 5501</em>,
|
||||||
|
but you can let the script control FlightGear on another host and
|
||||||
|
under another port.</p>
|
||||||
|
|
||||||
|
<blockquote>
|
||||||
|
<pre>
|
||||||
|
$ fgfsscript some.host.org 1234&
|
||||||
|
</pre>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
|
||||||
|
<p>Demo programs are available in:<br>
|
||||||
|
<ul>
|
||||||
|
<li><a href="fgfsclient.c">C</a></li>
|
||||||
|
<li><a href="fgfsclient.cxx">C++</a></li>
|
||||||
|
<li><a href="fgfsscript">Perl (same as mentioned above)</a></li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<hr>
|
||||||
|
<address>
|
||||||
|
$Id$<br>
|
||||||
|
<a href="http://www.unet.univie.ac.at/~a8603365/">Melchior FRANZ</a>
|
||||||
|
(mfranz at aon dot at</a>)
|
||||||
|
</address>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue