2006-02-18 13:58:09 +00:00
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
2013-02-08 08:46:34 +01:00
# ifdef HAVE_WINDOWS_H
# include <windows.h>
# endif
2007-05-14 16:24:41 +00:00
# ifdef HAVE_SYS_TIME_H
# include <sys / time.h> // gettimeofday
# endif
2003-11-25 21:08:36 +00:00
# include <string.h>
2015-03-13 18:02:46 +00:00
# include <errno.h>
2003-11-25 21:08:36 +00:00
# include <stdio.h>
# include <sys/types.h>
# include <sys/stat.h>
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
# include <fstream>
2009-03-22 17:04:02 +00:00
# include <sstream>
2003-11-25 21:08:36 +00:00
# include <simgear/nasal/nasal.h>
2015-03-13 18:02:46 +00:00
# include <simgear/nasal/iolib.h>
2003-11-25 21:08:36 +00:00
# include <simgear/props/props.hxx>
2003-12-05 01:54:39 +00:00
# include <simgear/math/sg_random.h>
2003-11-25 21:08:36 +00:00
# include <simgear/misc/sg_path.hxx>
2010-10-24 07:09:05 +01:00
# include <simgear/misc/sg_dir.hxx>
2014-06-13 19:16:26 +02:00
# include <simgear/misc/SimpleMarkdown.hxx>
2003-11-25 21:08:36 +00:00
# include <simgear/structure/commands.hxx>
2007-06-16 18:22:20 +00:00
# include <simgear/math/sg_geodesy.hxx>
2008-07-31 12:04:32 +00:00
# include <simgear/structure/event_mgr.hxx>
2013-02-08 19:39:41 +00:00
# include <simgear/debug/BufferedLogCallback.hxx>
2013-03-16 12:44:27 +00:00
# include <simgear/nasal/cppbind/from_nasal.hxx>
# include <simgear/nasal/cppbind/to_nasal.hxx>
# include <simgear/nasal/cppbind/Ghost.hxx>
2013-02-09 16:05:54 +00:00
# include <simgear/nasal/cppbind/NasalHash.hxx>
2003-11-25 21:08:36 +00:00
2013-12-17 19:07:08 +01:00
# include "NasalSGPath.hxx"
2012-04-15 13:54:50 +01:00
# include "NasalSys.hxx"
2013-02-09 15:33:05 +00:00
# include "NasalSys_private.hxx"
2014-05-10 10:52:16 +02:00
# include "NasalAircraft.hxx"
2013-02-09 15:33:05 +00:00
# include "NasalModelData.hxx"
2012-04-15 13:54:50 +01:00
# include "NasalPositioned.hxx"
2012-08-02 12:18:38 +01:00
# include "NasalCanvas.hxx"
2012-08-04 00:24:26 +02:00
# include "NasalClipboard.hxx"
2012-08-19 21:13:31 +01:00
# include "NasalCondition.hxx"
2013-10-15 01:02:05 +02:00
# include "NasalHTTP.hxx"
2013-01-31 19:14:14 +01:00
# include "NasalString.hxx"
2012-08-02 12:18:38 +01:00
2003-11-25 21:08:36 +00:00
# include <Main/globals.hxx>
2008-07-22 20:26:17 +00:00
# include <Main/util.hxx>
2012-04-15 13:54:50 +01:00
# include <Main/fg_props.hxx>
2012-08-04 00:24:26 +02:00
2012-03-31 18:19:14 +02:00
using std : : map ;
2013-11-11 19:55:54 +01:00
using std : : string ;
using std : : vector ;
2012-03-31 18:19:14 +02:00
2012-12-28 14:48:19 +00:00
void postinitNasalGUI ( naRef globals , naContext c ) ;
2007-05-01 17:03:50 +00:00
static FGNasalSys * nasalSys = 0 ;
2011-04-03 15:30:25 +02:00
// Listener class for loading Nasal modules on demand
class FGNasalModuleListener : public SGPropertyChangeListener
{
public :
FGNasalModuleListener ( SGPropertyNode * node ) ;
virtual void valueChanged ( SGPropertyNode * node ) ;
private :
SGPropertyNode_ptr _node ;
} ;
FGNasalModuleListener : : FGNasalModuleListener ( SGPropertyNode * node ) : _node ( node )
{
}
void FGNasalModuleListener : : valueChanged ( SGPropertyNode * )
{
if ( _node - > getBoolValue ( " enabled " , false ) & &
! _node - > getBoolValue ( " loaded " , true ) )
{
nasalSys - > loadPropertyScripts ( _node ) ;
}
}
2013-03-16 12:44:27 +00:00
//////////////////////////////////////////////////////////////////////////
class TimerObj : public SGReferenced
{
public :
TimerObj ( FGNasalSys * sys , naRef f , naRef self , double interval ) :
_sys ( sys ) ,
_func ( f ) ,
_self ( self ) ,
_isRunning ( false ) ,
_interval ( interval ) ,
_singleShot ( false )
{
char nm [ 128 ] ;
snprintf ( nm , 128 , " nasal-timer-%p " , this ) ;
_name = nm ;
_gcRoot = sys - > gcSave ( f ) ;
_gcSelf = sys - > gcSave ( self ) ;
}
virtual ~ TimerObj ( )
{
stop ( ) ;
_sys - > gcRelease ( _gcRoot ) ;
_sys - > gcRelease ( _gcSelf ) ;
}
2013-03-20 18:10:27 +00:00
bool isRunning ( ) const { return _isRunning ; }
2013-03-16 12:44:27 +00:00
void stop ( )
{
if ( _isRunning ) {
globals - > get_event_mgr ( ) - > removeTask ( _name ) ;
_isRunning = false ;
}
}
void start ( )
{
if ( _isRunning ) {
return ;
}
_isRunning = true ;
if ( _singleShot ) {
globals - > get_event_mgr ( ) - > addEvent ( _name , this , & TimerObj : : invoke , _interval ) ;
} else {
2013-11-09 08:01:57 -08:00
globals - > get_event_mgr ( ) - > addTask ( _name , this , & TimerObj : : invoke ,
_interval , _interval /* delay */ ) ;
2013-03-16 12:44:27 +00:00
}
}
// stop and then start -
void restart ( double newInterval )
{
_interval = newInterval ;
stop ( ) ;
start ( ) ;
}
void invoke ( )
{
2014-08-11 00:31:18 +02:00
if ( _singleShot )
// Callback may restart the timer, so update status before callback is
// called (Prevent warnings of deleting not existing tasks from the
// event manager).
_isRunning = false ;
2013-03-16 12:44:27 +00:00
naRef * args = NULL ;
_sys - > callMethod ( _func , _self , 0 , args , naNil ( ) /* locals */ ) ;
}
void setSingleShot ( bool aSingleShot )
{
_singleShot = aSingleShot ;
}
bool isSingleShot ( ) const
{ return _singleShot ; }
private :
std : : string _name ;
FGNasalSys * _sys ;
naRef _func , _self ;
int _gcRoot , _gcSelf ;
bool _isRunning ;
double _interval ;
bool _singleShot ;
} ;
typedef SGSharedPtr < TimerObj > TimerObjRef ;
typedef nasal : : Ghost < TimerObjRef > NasalTimerObj ;
///////////////////////////////////////////////////////////////////////////
2007-05-01 17:03:50 +00:00
2003-11-25 21:08:36 +00:00
// Read and return file contents in a single buffer. Note use of
// stat() to get the file size. This is a win32 function, believe it
// or not. :) Note the REALLY IMPORTANT use of the "b" flag to fopen.
// Text mode brain damage will kill us if we're trying to do bytewise
// I/O.
static char * readfile ( const char * file , int * lenOut )
{
struct stat data ;
if ( stat ( file , & data ) ! = 0 ) return 0 ;
FILE * f = fopen ( file , " rb " ) ;
if ( ! f ) return 0 ;
char * buf = new char [ data . st_size ] ;
* lenOut = fread ( buf , 1 , data . st_size , f ) ;
fclose ( f ) ;
if ( * lenOut ! = data . st_size ) {
// Shouldn't happen, but warn anyway since it represents a
// platform bug and not a typical runtime error (missing file,
// etc...)
SG_LOG ( SG_NASAL , SG_ALERT ,
" ERROR in Nasal initialization: " < <
2003-12-01 14:35:49 +00:00
" short count returned from fread() of " < < file < <
" . Check your C library! " ) ;
2003-11-25 21:08:36 +00:00
delete [ ] buf ;
return 0 ;
}
return buf ;
}
2014-03-16 22:52:55 +00:00
FGNasalSys : : FGNasalSys ( ) :
_inited ( false )
2003-11-25 21:08:36 +00:00
{
2007-05-01 17:03:50 +00:00
nasalSys = this ;
2003-11-25 21:08:36 +00:00
_context = 0 ;
_globals = naNil ( ) ;
2013-01-31 19:14:14 +01:00
_string = naNil ( ) ;
2013-11-22 22:40:50 +00:00
_wrappedNodeFunc = naNil ( ) ;
2013-02-08 19:39:41 +00:00
_log = new simgear : : BufferedLogCallback ( SG_NASAL , SG_INFO ) ;
_log - > truncateAt ( 255 ) ;
sglog ( ) . addCallback ( _log ) ;
2013-10-15 17:48:13 +02:00
naSetErrorHandler ( & logError ) ;
2006-07-19 19:46:53 +00:00
}
2012-08-04 00:24:26 +02:00
// Utility. Sets a named key in a hash by C string, rather than nasal
// string object.
void FGNasalSys : : hashset ( naRef hash , const char * key , naRef val )
{
naRef s = naNewString ( _context ) ;
naStr_fromdata ( s , ( char * ) key , strlen ( key ) ) ;
naHash_set ( hash , s , val ) ;
}
void FGNasalSys : : globalsSet ( const char * key , naRef val )
{
hashset ( _globals , key , val ) ;
}
2012-05-12 17:23:17 +01:00
naRef FGNasalSys : : call ( naRef code , int argc , naRef * args , naRef locals )
{
return callMethod ( code , naNil ( ) , argc , args , locals ) ;
}
2014-04-15 14:13:46 +01:00
naRef FGNasalSys : : callWithContext ( naContext ctx , naRef code , int argc , naRef * args , naRef locals )
{
return callMethodWithContext ( ctx , code , naNil ( ) , argc , args , locals ) ;
}
2006-07-19 19:46:53 +00:00
// Does a naCall() in a new context. Wrapped here to make lock
// tracking easier. Extension functions are called with the lock, but
// we have to release it before making a new naCall(). So rather than
// drop the lock in every extension function that might call back into
// Nasal, we keep a stack depth counter here and only unlock/lock
// around the naCall if it isn't the first one.
2012-05-12 17:23:17 +01:00
naRef FGNasalSys : : callMethod ( naRef code , naRef self , int argc , naRef * args , naRef locals )
2006-07-19 19:46:53 +00:00
{
2013-10-15 17:48:13 +02:00
return naCallMethod ( code , self , argc , args , locals ) ;
2003-11-25 21:08:36 +00:00
}
2014-04-15 14:13:46 +01:00
naRef FGNasalSys : : callMethodWithContext ( naContext ctx , naRef code , naRef self , int argc , naRef * args , naRef locals )
{
return naCallMethodCtx ( ctx , code , self , argc , args , locals ) ;
}
2003-11-25 21:08:36 +00:00
FGNasalSys : : ~ FGNasalSys ( )
{
2014-03-16 22:52:55 +00:00
if ( _inited ) {
SG_LOG ( SG_GENERAL , SG_ALERT , " Nasal was not shutdown " ) ;
}
2007-05-01 17:03:50 +00:00
nasalSys = 0 ;
2003-11-25 21:08:36 +00:00
}
2003-12-01 14:35:49 +00:00
bool FGNasalSys : : parseAndRun ( const char * sourceCode )
{
2014-04-15 14:13:46 +01:00
naContext ctx = naNewContext ( ) ;
naRef code = parse ( ctx , " FGNasalSys::parseAndRun() " , sourceCode ,
2003-12-01 14:35:49 +00:00
strlen ( sourceCode ) ) ;
2014-04-15 14:13:46 +01:00
if ( naIsNil ( code ) ) {
naFreeContext ( ctx ) ;
2003-12-01 14:35:49 +00:00
return false ;
2014-04-15 14:13:46 +01:00
}
callWithContext ( ctx , code , 0 , 0 , naNil ( ) ) ;
naFreeContext ( ctx ) ;
2006-07-19 19:46:53 +00:00
return true ;
2003-12-01 14:35:49 +00:00
}
2013-02-09 15:33:05 +00:00
#if 0
2003-12-05 01:54:39 +00:00
FGNasalScript * FGNasalSys : : parseScript ( const char * src , const char * name )
{
FGNasalScript * script = new FGNasalScript ( ) ;
script - > _gcKey = - 1 ; // important, if we delete it on a parse
script - > _nas = this ; // error, don't clobber a real handle!
char buf [ 256 ] ;
if ( ! name ) {
2007-05-17 18:43:36 +00:00
sprintf ( buf , " FGNasalScript@%p " , ( void * ) script ) ;
2003-12-05 01:54:39 +00:00
name = buf ;
}
2005-04-26 20:57:06 +00:00
script - > _code = parse ( name , src , strlen ( src ) ) ;
2003-12-05 01:54:39 +00:00
if ( naIsNil ( script - > _code ) ) {
delete script ;
return 0 ;
}
script - > _gcKey = gcSave ( script - > _code ) ;
return script ;
}
2013-02-09 15:33:05 +00:00
# endif
2003-12-05 01:54:39 +00:00
2003-11-25 21:08:36 +00:00
// The get/setprop functions accept a *list* of strings and walk
// through the property tree with them to find the appropriate node.
// This allows a Nasal object to hold onto a property path and use it
// like a node object, e.g. setprop(ObjRoot, "size-parsecs", 2.02). This
// is the utility function that walks the property tree.
2014-02-18 20:07:29 -08:00
static SGPropertyNode * findnode ( naContext c , naRef * vec , int len , bool create = false )
2003-11-25 21:08:36 +00:00
{
SGPropertyNode * p = globals - > get_props ( ) ;
2006-05-23 18:55:38 +00:00
try {
for ( int i = 0 ; i < len ; i + + ) {
naRef a = vec [ i ] ;
2014-02-18 20:07:29 -08:00
if ( ! naIsString ( a ) ) {
naRuntimeError ( c , " bad argument to setprop/getprop path: expected a string " ) ;
}
naRef b = i < len - 1 ? naNumValue ( vec [ i + 1 ] ) : naNil ( ) ;
if ( ! naIsNil ( b ) ) {
p = p - > getNode ( naStr_data ( a ) , ( int ) b . num , create ) ;
i + + ;
} else {
p = p - > getNode ( naStr_data ( a ) , create ) ;
}
2006-05-23 18:55:38 +00:00
if ( p = = 0 ) return 0 ;
}
} catch ( const string & err ) {
naRuntimeError ( c , ( char * ) err . c_str ( ) ) ;
2003-11-25 21:08:36 +00:00
}
return p ;
}
// getprop() extension function. Concatenates its string arguments as
// property names and returns the value of the specified property. Or
// nil if it doesn't exist.
2005-04-18 19:49:13 +00:00
static naRef f_getprop ( naContext c , naRef me , int argc , naRef * args )
2003-11-25 21:08:36 +00:00
{
2009-07-17 14:54:12 +02:00
using namespace simgear ;
2014-02-18 20:07:29 -08:00
if ( argc < 1 ) {
naRuntimeError ( c , " getprop() expects at least 1 argument " ) ;
}
const SGPropertyNode * p = findnode ( c , args , argc , false ) ;
2003-11-25 21:08:36 +00:00
if ( ! p ) return naNil ( ) ;
switch ( p - > getType ( ) ) {
2009-07-17 14:54:12 +02:00
case props : : BOOL : case props : : INT :
case props : : LONG : case props : : FLOAT :
case props : : DOUBLE :
2010-10-20 00:08:57 +01:00
{
double dv = p - > getDoubleValue ( ) ;
2012-11-11 17:22:42 +01:00
if ( SGMisc < double > : : isNaN ( dv ) ) {
2011-12-11 13:55:56 +01:00
SG_LOG ( SG_NASAL , SG_ALERT , " Nasal getprop: property " < < p - > getPath ( ) < < " is NaN " ) ;
2011-01-14 20:04:20 +00:00
return naNil ( ) ;
2010-10-20 00:08:57 +01:00
}
return naNum ( dv ) ;
}
2009-07-17 14:54:12 +02:00
case props : : STRING :
case props : : UNSPECIFIED :
2003-11-25 21:08:36 +00:00
{
naRef nastr = naNewString ( c ) ;
const char * val = p - > getStringValue ( ) ;
naStr_fromdata ( nastr , ( char * ) val , strlen ( val ) ) ;
return nastr ;
}
2009-07-17 14:54:12 +02:00
case props : : ALIAS : // <--- FIXME, recurse?
2003-11-25 21:08:36 +00:00
default :
return naNil ( ) ;
}
}
// setprop() extension function. Concatenates its string arguments as
// property names and sets the value of the specified property to the
// final argument.
2005-04-18 19:49:13 +00:00
static naRef f_setprop ( naContext c , naRef me , int argc , naRef * args )
2003-11-25 21:08:36 +00:00
{
2014-02-18 20:07:29 -08:00
if ( argc < 2 ) {
naRuntimeError ( c , " setprop() expects at least 2 arguments " ) ;
2003-11-25 21:08:36 +00:00
}
2014-02-18 20:07:29 -08:00
naRef val = args [ argc - 1 ] ;
SGPropertyNode * p = findnode ( c , args , argc - 1 , true ) ;
2003-11-25 21:08:36 +00:00
2009-02-07 23:52:12 +00:00
bool result = false ;
2006-05-23 18:55:38 +00:00
try {
2014-02-18 20:07:29 -08:00
if ( naIsString ( val ) ) result = p - > setStringValue ( naStr_data ( val ) ) ;
2006-10-17 19:58:33 +00:00
else {
2014-02-18 20:07:29 -08:00
if ( ! naIsNum ( val ) )
2006-10-17 19:58:33 +00:00
naRuntimeError ( c , " setprop() value is not string or number " ) ;
2010-10-20 00:08:57 +01:00
2014-02-18 20:07:29 -08:00
if ( SGMisc < double > : : isNaN ( val . num ) ) {
2010-10-20 00:08:57 +01:00
naRuntimeError ( c , " setprop() passed a NaN " ) ;
}
2014-02-18 20:07:29 -08:00
result = p - > setDoubleValue ( val . num ) ;
2006-10-17 19:58:33 +00:00
}
2006-05-23 18:55:38 +00:00
} catch ( const string & err ) {
naRuntimeError ( c , ( char * ) err . c_str ( ) ) ;
}
2009-02-07 23:52:12 +00:00
return naNum ( result ) ;
2003-11-25 21:08:36 +00:00
}
// print() extension function. Concatenates and prints its arguments
// to the FlightGear log. Uses the highest log level (SG_ALERT), to
// make sure it appears. Is there better way to do this?
2005-04-18 19:49:13 +00:00
static naRef f_print ( naContext c , naRef me , int argc , naRef * args )
2003-11-25 21:08:36 +00:00
{
2007-01-28 12:16:37 +00:00
string buf ;
2005-04-18 19:49:13 +00:00
int n = argc ;
2003-11-25 21:08:36 +00:00
for ( int i = 0 ; i < n ; i + + ) {
2005-04-18 19:49:13 +00:00
naRef s = naStringValue ( c , args [ i ] ) ;
2003-11-25 21:08:36 +00:00
if ( naIsNil ( s ) ) continue ;
2007-01-28 12:16:37 +00:00
buf + = naStr_data ( s ) ;
2003-11-25 21:08:36 +00:00
}
2011-12-11 13:55:56 +01:00
SG_LOG ( SG_NASAL , SG_ALERT , buf ) ;
2007-01-28 12:16:37 +00:00
return naNum ( buf . length ( ) ) ;
2003-11-25 21:08:36 +00:00
}
2013-02-07 16:44:24 +00:00
// logprint() extension function. Same as above, all arguments after the
// first argument are concatenated. Argument 0 is the log-level, matching
// sgDebugPriority.
static naRef f_logprint ( naContext c , naRef me , int argc , naRef * args )
{
if ( argc < 1 )
naRuntimeError ( c , " no prioirty argument to logprint() " ) ;
naRef priority = args [ 0 ] ;
string buf ;
int n = argc ;
for ( int i = 1 ; i < n ; i + + ) {
naRef s = naStringValue ( c , args [ i ] ) ;
if ( naIsNil ( s ) ) continue ;
buf + = naStr_data ( s ) ;
}
2013-02-09 15:33:05 +00:00
// use the nasal source file and line for the message location, since
// that's more useful than the location here!
sglog ( ) . log ( SG_NASAL , ( sgDebugPriority ) ( int ) priority . num ,
naStr_data ( naGetSourceFile ( c , 0 ) ) ,
naGetLine ( c , 0 ) , buf ) ;
2013-02-07 16:44:24 +00:00
return naNum ( buf . length ( ) ) ;
}
2003-11-25 21:08:36 +00:00
// fgcommand() extension function. Executes a named command via the
// FlightGear command manager. Takes a single property node name as
// an argument.
2005-04-18 19:49:13 +00:00
static naRef f_fgcommand ( naContext c , naRef me , int argc , naRef * args )
2003-11-25 21:08:36 +00:00
{
2007-06-07 16:17:48 +00:00
naRef cmd = argc > 0 ? args [ 0 ] : naNil ( ) ;
naRef props = argc > 1 ? args [ 1 ] : naNil ( ) ;
if ( ! naIsString ( cmd ) | | ( ! naIsNil ( props ) & & ! naIsGhost ( props ) ) )
2005-04-18 19:49:13 +00:00
naRuntimeError ( c , " bad arguments to fgcommand() " ) ;
2014-11-23 14:53:54 +01:00
SGPropertyNode_ptr node ;
2007-06-07 16:17:48 +00:00
if ( ! naIsNil ( props ) )
2014-11-23 14:53:54 +01:00
node = static_cast < SGPropertyNode * > ( naGhost_ptr ( props ) ) ;
else
node = new SGPropertyNode ;
return naNum ( globals - > get_commands ( ) - > execute ( naStr_data ( cmd ) , node ) ) ;
2003-11-25 21:08:36 +00:00
}
// settimer(func, dt, simtime) extension function. Falls through to
// FGNasalSys::setTimer(). See there for docs.
2005-04-18 19:49:13 +00:00
static naRef f_settimer ( naContext c , naRef me , int argc , naRef * args )
2003-11-25 21:08:36 +00:00
{
2007-05-12 18:15:45 +00:00
nasalSys - > setTimer ( c , argc , args ) ;
2003-11-25 21:08:36 +00:00
return naNil ( ) ;
}
2013-03-16 12:44:27 +00:00
static naRef f_makeTimer ( naContext c , naRef me , int argc , naRef * args )
{
if ( ! naIsNum ( args [ 0 ] ) ) {
naRuntimeError ( c , " bad interval argument to maketimer " ) ;
}
naRef func , self = naNil ( ) ;
if ( naIsFunc ( args [ 1 ] ) ) {
func = args [ 1 ] ;
} else if ( ( argc = = 3 ) & & naIsFunc ( args [ 2 ] ) ) {
self = args [ 1 ] ;
func = args [ 2 ] ;
}
TimerObj * timerObj = new TimerObj ( nasalSys , func , self , args [ 0 ] . num ) ;
2014-06-03 02:23:17 +02:00
return nasal : : to_nasal ( c , timerObj ) ;
2013-03-16 12:44:27 +00:00
}
2006-02-28 14:55:37 +00:00
// setlistener(func, property, bool) extension function. Falls through to
2005-12-16 19:11:03 +00:00
// FGNasalSys::setListener(). See there for docs.
static naRef f_setlistener ( naContext c , naRef me , int argc , naRef * args )
{
2007-05-01 17:03:50 +00:00
return nasalSys - > setListener ( c , argc , args ) ;
2006-02-28 14:55:37 +00:00
}
// removelistener(int) extension function. Falls through to
// FGNasalSys::removeListener(). See there for docs.
static naRef f_removelistener ( naContext c , naRef me , int argc , naRef * args )
{
2007-05-01 17:03:50 +00:00
return nasalSys - > removeListener ( c , argc , args ) ;
2005-12-16 19:11:03 +00:00
}
2003-12-01 14:35:49 +00:00
// Returns a ghost handle to the argument to the currently executing
// command
2005-04-18 19:49:13 +00:00
static naRef f_cmdarg ( naContext c , naRef me , int argc , naRef * args )
2003-12-01 14:35:49 +00:00
{
2007-05-01 17:03:50 +00:00
return nasalSys - > cmdArgGhost ( ) ;
2003-12-01 14:35:49 +00:00
}
2003-12-05 01:54:39 +00:00
// Sets up a property interpolation. The first argument is either a
2014-11-23 14:53:54 +01:00
// ghost (SGPropertyNode*) or a string (global property path) to
2003-12-05 01:54:39 +00:00
// interpolate. The second argument is a vector of pairs of
// value/delta numbers.
2005-04-18 19:49:13 +00:00
static naRef f_interpolate ( naContext c , naRef me , int argc , naRef * args )
2003-12-05 01:54:39 +00:00
{
2013-03-16 16:43:55 +01:00
SGPropertyNode * node ;
naRef prop = argc > 0 ? args [ 0 ] : naNil ( ) ;
if ( naIsString ( prop ) ) node = fgGetNode ( naStr_data ( prop ) , true ) ;
2014-11-23 14:53:54 +01:00
else if ( naIsGhost ( prop ) ) node = static_cast < SGPropertyNode * > ( naGhost_ptr ( prop ) ) ;
2013-03-16 16:43:55 +01:00
else return naNil ( ) ;
2004-11-15 18:15:33 +00:00
2013-03-16 16:43:55 +01:00
naRef curve = argc > 1 ? args [ 1 ] : naNil ( ) ;
if ( ! naIsVector ( curve ) ) return naNil ( ) ;
int nPoints = naVec_size ( curve ) / 2 ;
simgear : : PropertyList value_nodes ;
value_nodes . reserve ( nPoints ) ;
double_list deltas ;
deltas . reserve ( nPoints ) ;
for ( int i = 0 ; i < nPoints ; + + i )
{
SGPropertyNode * val = new SGPropertyNode ;
val - > setDoubleValue ( naNumValue ( naVec_get ( curve , 2 * i ) ) . num ) ;
value_nodes . push_back ( val ) ;
deltas . push_back ( naNumValue ( naVec_get ( curve , 2 * i + 1 ) ) . num ) ;
}
2013-03-17 23:48:42 +01:00
node - > interpolate ( " numeric " , value_nodes , deltas , " linear " ) ;
2013-03-16 16:43:55 +01:00
return naNil ( ) ;
2003-12-05 01:54:39 +00:00
}
2005-04-18 19:49:13 +00:00
// This is a better RNG than the one in the default Nasal distribution
// (which is based on the C library rand() implementation). It will
// override.
static naRef f_rand ( naContext c , naRef me , int argc , naRef * args )
2003-12-05 01:54:39 +00:00
{
return naNum ( sg_random ( ) ) ;
}
2006-01-09 15:29:24 +00:00
static naRef f_srand ( naContext c , naRef me , int argc , naRef * args )
{
sg_srandom_time ( ) ;
return naNum ( 0 ) ;
}
2008-06-19 17:18:42 +00:00
static naRef f_abort ( naContext c , naRef me , int argc , naRef * args )
{
abort ( ) ;
return naNil ( ) ;
}
2006-01-09 03:48:14 +00:00
// Return an array listing of all files in a directory
static naRef f_directory ( naContext c , naRef me , int argc , naRef * args )
{
if ( argc ! = 1 | | ! naIsString ( args [ 0 ] ) )
naRuntimeError ( c , " bad arguments to directory() " ) ;
2010-10-24 07:09:05 +01:00
simgear : : Dir d ( SGPath ( naStr_data ( args [ 0 ] ) ) ) ;
if ( ! d . exists ( ) ) return naNil ( ) ;
2006-01-09 03:48:14 +00:00
naRef result = naNewVector ( c ) ;
2010-10-24 07:09:05 +01:00
simgear : : PathList paths = d . children ( simgear : : Dir : : TYPE_FILE | simgear : : Dir : : TYPE_DIR ) ;
for ( unsigned int i = 0 ; i < paths . size ( ) ; + + i ) {
std : : string p = paths [ i ] . file ( ) ;
naVec_append ( result , naStr_fromdata ( naNewString ( c ) , p . c_str ( ) , p . size ( ) ) ) ;
}
2006-01-09 03:48:14 +00:00
return result ;
}
2010-08-15 11:02:27 +01:00
/**
* Given a data path , resolve it in FG_ROOT or an FG_AIRCRFT directory
*/
static naRef f_resolveDataPath ( naContext c , naRef me , int argc , naRef * args )
{
if ( argc ! = 1 | | ! naIsString ( args [ 0 ] ) )
naRuntimeError ( c , " bad arguments to resolveDataPath() " ) ;
SGPath p = globals - > resolve_maybe_aircraft_path ( naStr_data ( args [ 0 ] ) ) ;
const char * pdata = p . c_str ( ) ;
return naStr_fromdata ( naNewString ( c ) , const_cast < char * > ( pdata ) , strlen ( pdata ) ) ;
}
2013-06-27 09:37:53 +01:00
static naRef f_findDataDir ( naContext c , naRef me , int argc , naRef * args )
{
if ( argc ! = 1 | | ! naIsString ( args [ 0 ] ) )
naRuntimeError ( c , " bad arguments to findDataDir() " ) ;
SGPath p = globals - > find_data_dir ( naStr_data ( args [ 0 ] ) ) ;
const char * pdata = p . c_str ( ) ;
return naStr_fromdata ( naNewString ( c ) , const_cast < char * > ( pdata ) , strlen ( pdata ) ) ;
}
2013-02-09 15:33:05 +00:00
class NasalCommand : public SGCommandMgr : : Command
{
public :
2013-10-06 17:36:19 +01:00
NasalCommand ( FGNasalSys * sys , naRef f , const std : : string & name ) :
2013-02-09 15:33:05 +00:00
_sys ( sys ) ,
2013-10-06 17:36:19 +01:00
_func ( f ) ,
_name ( name )
2013-02-09 15:33:05 +00:00
{
2013-10-06 17:36:19 +01:00
globals - > get_commands ( ) - > addCommandObject ( _name , this ) ;
2013-02-09 15:33:05 +00:00
_gcRoot = sys - > gcSave ( f ) ;
}
virtual ~ NasalCommand ( )
{
_sys - > gcRelease ( _gcRoot ) ;
}
virtual bool operator ( ) ( const SGPropertyNode * aNode )
{
_sys - > setCmdArg ( const_cast < SGPropertyNode * > ( aNode ) ) ;
naRef args [ 1 ] ;
2013-02-09 16:05:54 +00:00
args [ 0 ] = _sys - > wrappedPropsNode ( const_cast < SGPropertyNode * > ( aNode ) ) ;
2013-02-09 15:33:05 +00:00
_sys - > callMethod ( _func , naNil ( ) , 1 , args , naNil ( ) /* locals */ ) ;
return true ;
}
private :
FGNasalSys * _sys ;
naRef _func ;
int _gcRoot ;
2013-10-06 17:36:19 +01:00
std : : string _name ;
2013-02-09 15:33:05 +00:00
} ;
static naRef f_addCommand ( naContext c , naRef me , int argc , naRef * args )
{
if ( argc ! = 2 | | ! naIsString ( args [ 0 ] ) | | ! naIsFunc ( args [ 1 ] ) )
naRuntimeError ( c , " bad arguments to addcommand() " ) ;
2013-10-06 17:36:19 +01:00
nasalSys - > addCommand ( args [ 1 ] , naStr_data ( args [ 0 ] ) ) ;
2013-02-09 15:33:05 +00:00
return naNil ( ) ;
}
static naRef f_removeCommand ( naContext c , naRef me , int argc , naRef * args )
{
2013-10-06 17:36:19 +01:00
if ( ( argc < 1 ) | | ! naIsString ( args [ 0 ] ) )
naRuntimeError ( c , " bad argument to removecommand() " ) ;
2013-02-09 15:33:05 +00:00
2013-10-06 17:36:19 +01:00
globals - > get_commands ( ) - > removeCommand ( naStr_data ( args [ 0 ] ) ) ;
2013-02-09 15:33:05 +00:00
return naNil ( ) ;
}
2015-03-13 18:02:46 +00:00
static naRef f_open ( naContext c , naRef me , int argc , naRef * args )
{
FILE * f ;
naRef file = argc > 0 ? naStringValue ( c , args [ 0 ] ) : naNil ( ) ;
naRef mode = argc > 1 ? naStringValue ( c , args [ 1 ] ) : naNil ( ) ;
if ( ! naStr_data ( file ) ) naRuntimeError ( c , " bad argument to open() " ) ;
const char * modestr = naStr_data ( mode ) ? naStr_data ( mode ) : " rb " ;
std : : string filename = fgValidatePath ( naStr_data ( file ) ,
strcmp ( modestr , " rb " ) & & strcmp ( modestr , " r " ) ) ;
if ( filename . empty ( ) ) {
naRuntimeError ( c , " open(): reading/writing '%s' denied "
2015-07-25 10:15:31 +01:00
" (unauthorized directory - authorization no longer follows symlinks; to authorize reading additional directories, add them to --fg-aircraft) " , naStr_data ( file ) ) ;
2015-03-13 18:02:46 +00:00
return naNil ( ) ;
}
f = fopen ( filename . c_str ( ) , modestr ) ;
if ( ! f ) naRuntimeError ( c , strerror ( errno ) ) ;
return naIOGhost ( c , f ) ;
}
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
// Parse XML file.
// parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
//
2007-09-30 11:56:21 +00:00
// <path> ... absolute path to an XML file
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
// <start-tag> ... callback function with two args: tag name, attribute hash
// <end-tag> ... callback function with one arg: tag name
2007-07-01 11:07:53 +00:00
// <data> ... callback function with one arg: data
// <pi> ... callback function with two args: target, data
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
// (pi = "processing instruction")
// All four callback functions are optional and default to nil.
2008-07-22 20:26:17 +00:00
// The function returns nil on error, or the validated file name otherwise.
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
static naRef f_parsexml ( naContext c , naRef me , int argc , naRef * args )
{
if ( argc < 1 | | ! naIsString ( args [ 0 ] ) )
2007-07-01 11:07:53 +00:00
naRuntimeError ( c , " parsexml(): path argument missing or not a string " ) ;
if ( argc > 5 ) argc = 5 ;
for ( int i = 1 ; i < argc ; i + + )
if ( ! ( naIsNil ( args [ i ] ) | | naIsFunc ( args [ i ] ) ) )
naRuntimeError ( c , " parsexml(): callback argument not a function " ) ;
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
2015-03-13 18:07:24 +00:00
std : : string file = fgValidatePath ( naStr_data ( args [ 0 ] ) , false ) ;
if ( file . empty ( ) ) {
2008-07-22 20:26:17 +00:00
naRuntimeError ( c , " parsexml(): reading '%s' denied "
2015-07-25 10:15:31 +01:00
" (unauthorized directory - authorization no longer follows symlinks; to authorize reading additional directories, add them to --fg-aircraft) " , naStr_data ( args [ 0 ] ) ) ;
2008-07-22 20:26:17 +00:00
return naNil ( ) ;
}
2015-03-13 18:07:24 +00:00
std : : ifstream input ( file . c_str ( ) ) ;
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
NasalXMLVisitor visitor ( c , argc , args ) ;
try {
readXML ( input , visitor ) ;
} catch ( const sg_exception & e ) {
2007-10-14 07:51:11 +00:00
naRuntimeError ( c , " parsexml(): file '%s' %s " ,
2015-03-13 18:07:24 +00:00
file . c_str ( ) , e . getFormattedMessage ( ) . c_str ( ) ) ;
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
return naNil ( ) ;
}
2015-03-13 18:07:24 +00:00
return naStr_fromdata ( naNewString ( c ) , file . c_str ( ) , file . length ( ) ) ;
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
}
2014-06-13 19:16:26 +02:00
/**
* Parse very simple and small subset of markdown
*
* parse_markdown ( src )
*/
static naRef f_parse_markdown ( naContext c , naRef me , int argc , naRef * args )
{
2014-07-21 00:26:54 +02:00
nasal : : CallContext ctx ( c , me , argc , args ) ;
2014-06-13 19:16:26 +02:00
return ctx . to_nasal (
simgear : : SimpleMarkdown : : parse ( ctx . requireArg < std : : string > ( 0 ) )
) ;
}
2014-06-17 22:33:53 +02:00
/**
* Create md5 hash from given string
*
* md5 ( str )
*/
static naRef f_md5 ( naContext c , naRef me , int argc , naRef * args )
{
2014-06-18 15:59:41 +02:00
if ( argc ! = 1 | | ! naIsString ( args [ 0 ] ) )
naRuntimeError ( c , " md5(): wrong type or number of arguments " ) ;
2014-06-17 22:33:53 +02:00
2014-06-18 15:59:41 +02:00
return nasal : : to_nasal (
c ,
simgear : : strutils : : md5 ( naStr_data ( args [ 0 ] ) , naStr_len ( args [ 0 ] ) )
) ;
2014-06-17 22:33:53 +02:00
}
2007-05-14 16:24:41 +00:00
// Return UNIX epoch time in seconds.
static naRef f_systime ( naContext c , naRef me , int argc , naRef * args )
{
2010-01-23 22:26:30 +00:00
# ifdef _WIN32
2007-05-14 16:24:41 +00:00
FILETIME ft ;
GetSystemTimeAsFileTime ( & ft ) ;
double t = ( 4294967296.0 * ft . dwHighDateTime + ft . dwLowDateTime ) ;
// Converts from 100ns units in 1601 epoch to unix epoch in sec
return naNum ( ( t * 1e-7 ) - 11644473600.0 ) ;
# else
struct timeval td ;
2011-06-02 23:53:42 +02:00
gettimeofday ( & td , 0 ) ;
return naNum ( td . tv_sec + 1e-6 * td . tv_usec ) ;
2007-05-14 16:24:41 +00:00
# endif
}
2003-11-25 21:08:36 +00:00
// Table of extension functions. Terminate with zeros.
2007-10-15 16:28:40 +00:00
static struct { const char * name ; naCFunction func ; } funcs [ ] = {
2003-11-25 21:08:36 +00:00
{ " getprop " , f_getprop } ,
{ " setprop " , f_setprop } ,
{ " print " , f_print } ,
2013-02-07 16:44:24 +00:00
{ " logprint " , f_logprint } ,
2003-12-01 14:35:49 +00:00
{ " _fgcommand " , f_fgcommand } ,
2003-11-25 21:08:36 +00:00
{ " settimer " , f_settimer } ,
2013-03-16 12:44:27 +00:00
{ " maketimer " , f_makeTimer } ,
2005-12-16 19:11:03 +00:00
{ " _setlistener " , f_setlistener } ,
2006-02-28 14:55:37 +00:00
{ " removelistener " , f_removelistener } ,
2013-02-09 15:33:05 +00:00
{ " addcommand " , f_addCommand } ,
{ " removecommand " , f_removeCommand } ,
2003-12-01 14:35:49 +00:00
{ " _cmdarg " , f_cmdarg } ,
2003-12-05 01:54:39 +00:00
{ " _interpolate " , f_interpolate } ,
{ " rand " , f_rand } ,
2006-01-09 15:29:24 +00:00
{ " srand " , f_srand } ,
2008-06-19 17:18:42 +00:00
{ " abort " , f_abort } ,
2006-01-09 03:48:14 +00:00
{ " directory " , f_directory } ,
2010-08-15 11:02:27 +01:00
{ " resolvepath " , f_resolveDataPath } ,
2013-06-27 09:37:53 +01:00
{ " finddata " , f_findDataDir } ,
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
{ " parsexml " , f_parsexml } ,
2014-06-13 19:16:26 +02:00
{ " parse_markdown " , f_parse_markdown } ,
2014-06-17 22:33:53 +02:00
{ " md5 " , f_md5 } ,
2007-05-14 16:24:41 +00:00
{ " systime " , f_systime } ,
2003-11-25 21:08:36 +00:00
{ 0 , 0 }
} ;
2003-12-01 14:35:49 +00:00
naRef FGNasalSys : : cmdArgGhost ( )
{
return propNodeGhost ( _cmdArg ) ;
}
2013-02-09 15:33:05 +00:00
void FGNasalSys : : setCmdArg ( SGPropertyNode * aNode )
{
_cmdArg = aNode ;
}
2003-11-25 21:08:36 +00:00
void FGNasalSys : : init ( )
{
2014-03-16 22:52:55 +00:00
if ( _inited ) {
SG_LOG ( SG_GENERAL , SG_ALERT , " duplicate init of Nasal " ) ;
}
2003-12-01 14:35:49 +00:00
int i ;
2003-11-25 21:08:36 +00:00
_context = naNewContext ( ) ;
// Start with globals. Add it to itself as a recursive
// sub-reference under the name "globals". This gives client-code
// write access to the namespace if someone wants to do something
// fancy.
2007-03-29 18:50:28 +00:00
_globals = naInit_std ( _context ) ;
2003-11-25 21:08:36 +00:00
naSave ( _context , _globals ) ;
hashset ( _globals , " globals " , _globals ) ;
2007-03-29 18:50:28 +00:00
hashset ( _globals , " math " , naInit_math ( _context ) ) ;
hashset ( _globals , " bits " , naInit_bits ( _context ) ) ;
hashset ( _globals , " io " , naInit_io ( _context ) ) ;
hashset ( _globals , " thread " , naInit_thread ( _context ) ) ;
hashset ( _globals , " utf8 " , naInit_utf8 ( _context ) ) ;
2006-03-15 18:10:29 +00:00
2003-11-25 21:08:36 +00:00
// Add our custom extension functions:
2003-12-01 14:35:49 +00:00
for ( i = 0 ; funcs [ i ] . name ; i + + )
2003-11-25 21:08:36 +00:00
hashset ( _globals , funcs [ i ] . name ,
naNewFunc ( _context , naNewCCode ( _context , funcs [ i ] . func ) ) ) ;
2015-03-13 18:02:46 +00:00
nasal : : Hash io_module = nasal : : Hash ( _globals , _context ) . get < nasal : : Hash > ( " io " ) ;
io_module . set ( " open " , f_open ) ;
2003-12-01 14:35:49 +00:00
// And our SGPropertyNode wrapper
hashset ( _globals , " props " , genPropsModule ( ) ) ;
2013-01-31 19:14:14 +01:00
// Add string methods
_string = naInit_string ( _context ) ;
naSave ( _context , _string ) ;
2013-10-15 00:58:04 +02:00
initNasalString ( _globals , _string , _context ) ;
2013-01-31 19:14:14 +01:00
2013-10-15 00:58:04 +02:00
initNasalPositioned ( _globals , _context ) ;
initNasalPositioned_cppbind ( _globals , _context ) ;
2014-05-10 10:52:16 +02:00
initNasalAircraft ( _globals , _context ) ;
2012-08-04 00:24:26 +02:00
NasalClipboard : : init ( this ) ;
2013-10-15 00:58:04 +02:00
initNasalCanvas ( _globals , _context ) ;
initNasalCondition ( _globals , _context ) ;
2013-10-15 01:02:05 +02:00
initNasalHTTP ( _globals , _context ) ;
2013-12-17 19:07:08 +01:00
initNasalSGPath ( _globals , _context ) ;
2012-08-19 21:13:31 +01:00
2013-11-23 20:08:55 +00:00
NasalTimerObj : : init ( " Timer " )
. method ( " start " , & TimerObj : : start )
. method ( " stop " , & TimerObj : : stop )
. method ( " restart " , & TimerObj : : restart )
. member ( " singleShot " , & TimerObj : : isSingleShot , & TimerObj : : setSingleShot )
. member ( " isRunning " , & TimerObj : : isRunning ) ;
2015-03-13 17:54:44 +00:00
// Set allowed paths for Nasal I/O
fgInitAllowedPaths ( ) ;
2003-11-25 21:08:36 +00:00
// Now load the various source files in the Nasal directory
2010-10-24 07:09:05 +01:00
simgear : : Dir nasalDir ( SGPath ( globals - > get_fg_root ( ) , " Nasal " ) ) ;
2011-04-02 15:13:29 +02:00
loadScriptDirectory ( nasalDir ) ;
// Add modules in Nasal subdirectories to property tree
simgear : : PathList directories = nasalDir . children ( simgear : : Dir : : TYPE_DIR +
simgear : : Dir : : NO_DOT_OR_DOTDOT , " " ) ;
for ( unsigned int i = 0 ; i < directories . size ( ) ; + + i ) {
simgear : : Dir dir ( directories [ i ] ) ;
simgear : : PathList scripts = dir . children ( simgear : : Dir : : TYPE_FILE , " .nas " ) ;
addModule ( directories [ i ] . file ( ) , scripts ) ;
2003-11-25 21:08:36 +00:00
}
2007-01-23 15:53:04 +00:00
// set signal and remove node to avoid restoring at reinit
const char * s = " nasal-dir-initialized " ;
SGPropertyNode * signal = fgGetNode ( " /sim/signals " , true ) ;
signal - > setBoolValue ( s , true ) ;
2014-03-06 00:41:41 +01:00
signal - > removeChildren ( s ) ;
2003-12-01 14:35:49 +00:00
// Pull scripts out of the property tree, too
loadPropertyScripts ( ) ;
2012-04-25 16:54:40 +01:00
// now Nasal modules are loaded, we can do some delayed work
postinitNasalPositioned ( _globals , _context ) ;
2012-12-28 14:48:19 +00:00
postinitNasalGUI ( _globals , _context ) ;
2014-03-16 22:52:55 +00:00
_inited = true ;
2003-12-01 14:35:49 +00:00
}
2013-10-06 17:36:19 +01:00
void FGNasalSys : : shutdown ( )
{
2014-03-16 22:52:55 +00:00
if ( ! _inited ) {
return ;
}
2013-10-06 17:36:19 +01:00
shutdownNasalPositioned ( ) ;
map < int , FGNasalListener * > : : iterator it , end = _listener . end ( ) ;
for ( it = _listener . begin ( ) ; it ! = end ; + + it )
delete it - > second ;
_listener . clear ( ) ;
NasalCommandDict : : iterator j = _commands . begin ( ) ;
for ( ; j ! = _commands . end ( ) ; + + j ) {
globals - > get_commands ( ) - > removeCommand ( j - > first ) ;
}
_commands . clear ( ) ;
2013-12-03 21:38:24 +00:00
std : : vector < FGNasalModuleListener * > : : iterator k = _moduleListeners . begin ( ) ;
for ( ; k ! = _moduleListeners . end ( ) ; + + k )
delete * k ;
_moduleListeners . clear ( ) ;
2013-10-06 17:36:19 +01:00
naClearSaved ( ) ;
_string = naNil ( ) ; // will be freed by _context
naFreeContext ( _context ) ;
//setWatchedRef(_globals);
// remove the recursive reference in globals
hashset ( _globals , " globals " , naNil ( ) ) ;
_globals = naNil ( ) ;
naGC ( ) ;
2014-03-16 22:52:55 +00:00
_inited = false ;
2013-10-06 17:36:19 +01:00
}
2013-02-09 16:05:54 +00:00
naRef FGNasalSys : : wrappedPropsNode ( SGPropertyNode * aProps )
{
2013-11-22 22:40:50 +00:00
if ( naIsNil ( _wrappedNodeFunc ) ) {
2013-03-21 01:16:01 +01:00
nasal : : Hash props = getGlobals ( ) . get < nasal : : Hash > ( " props " ) ;
2013-11-22 22:40:50 +00:00
_wrappedNodeFunc = props . get ( " wrapNode " ) ;
2013-02-09 16:05:54 +00:00
}
naRef args [ 1 ] ;
args [ 0 ] = propNodeGhost ( aProps ) ;
2013-11-22 22:40:50 +00:00
naContext ctx = naNewContext ( ) ;
2015-01-18 22:23:21 +00:00
naRef wrapped = naCallMethodCtx ( ctx , _wrappedNodeFunc , naNil ( ) , 1 , args , naNil ( ) ) ;
2013-11-22 22:40:50 +00:00
naFreeContext ( ctx ) ;
return wrapped ;
2013-02-09 16:05:54 +00:00
}
2007-01-23 16:47:04 +00:00
void FGNasalSys : : update ( double )
{
2012-08-05 11:19:24 +02:00
if ( NasalClipboard : : getInstance ( ) )
NasalClipboard : : getInstance ( ) - > update ( ) ;
2007-10-18 11:43:38 +00:00
if ( ! _dead_listener . empty ( ) ) {
vector < FGNasalListener * > : : iterator it , end = _dead_listener . end ( ) ;
for ( it = _dead_listener . begin ( ) ; it ! = end ; + + it ) delete * it ;
_dead_listener . clear ( ) ;
2007-01-23 16:47:04 +00:00
}
2007-10-24 18:07:02 +00:00
2012-02-06 22:19:33 +01:00
if ( ! _loadList . empty ( ) )
{
2013-03-23 12:53:17 +01:00
if ( _delay_load )
_delay_load = false ;
else
// process Nasal load hook (only one per update loop to avoid excessive lags)
_loadList . pop ( ) - > load ( ) ;
2012-02-06 22:19:33 +01:00
}
else
if ( ! _unloadList . empty ( ) )
{
// process pending Nasal unload hooks after _all_ load hooks were processed
// (only unload one per update loop to avoid excessive lags)
_unloadList . pop ( ) - > unload ( ) ;
}
2014-06-15 16:36:35 +02:00
// Destroy all queued ghosts
nasal : : ghostProcessDestroyList ( ) ;
2007-10-24 18:07:02 +00:00
// The global context is a legacy thing. We use dynamically
// created contexts for naCall() now, so that we can call them
// recursively. But there are still spots that want to use it for
// naNew*() calls, which end up leaking memory because the context
// only clears out its temporary vector when it's *used*. So just
// junk it and fetch a new/reinitialized one every frame. This is
// clumsy: the right solution would use the dynamic context in all
// cases and eliminate _context entirely. But that's more work,
// and this works fine (yes, they say "New" and "Free", but
// they're very fast, just trust me). -Andy
naFreeContext ( _context ) ;
_context = naNewContext ( ) ;
2007-01-23 16:47:04 +00:00
}
2011-05-29 18:44:15 +02:00
bool pathSortPredicate ( const SGPath & p1 , const SGPath & p2 )
{
return p1 . file ( ) < p2 . file ( ) ;
}
2011-04-02 15:13:29 +02:00
// Loads all scripts in given directory
void FGNasalSys : : loadScriptDirectory ( simgear : : Dir nasalDir )
{
simgear : : PathList scripts = nasalDir . children ( simgear : : Dir : : TYPE_FILE , " .nas " ) ;
2012-10-13 12:42:48 +02:00
// Note: simgear::Dir already reports file entries in a deterministic order,
// so a fixed loading sequence is guaranteed (same for every user)
2011-04-02 15:13:29 +02:00
for ( unsigned int i = 0 ; i < scripts . size ( ) ; + + i ) {
SGPath fullpath ( scripts [ i ] ) ;
SGPath file = fullpath . file ( ) ;
loadModule ( fullpath , file . base ( ) . c_str ( ) ) ;
}
}
// Create module with list of scripts
void FGNasalSys : : addModule ( string moduleName , simgear : : PathList scripts )
{
2013-03-21 19:43:14 -07:00
if ( ! scripts . empty ( ) )
2011-04-02 15:13:29 +02:00
{
SGPropertyNode * nasal = globals - > get_props ( ) - > getNode ( " nasal " ) ;
SGPropertyNode * module_node = nasal - > getChild ( moduleName , 0 , true ) ;
for ( unsigned int i = 0 ; i < scripts . size ( ) ; + + i ) {
SGPropertyNode * pFileNode = module_node - > getChild ( " file " , i , true ) ;
pFileNode - > setStringValue ( scripts [ i ] . c_str ( ) ) ;
}
if ( ! module_node - > hasChild ( " enabled " , 0 ) )
{
SGPropertyNode * node = module_node - > getChild ( " enabled " , 0 , true ) ;
node - > setBoolValue ( true ) ;
node - > setAttribute ( SGPropertyNode : : USERARCHIVE , true ) ;
}
}
}
2003-12-01 14:35:49 +00:00
// Loads the scripts found under /nasal in the global tree
void FGNasalSys : : loadPropertyScripts ( )
{
SGPropertyNode * nasal = globals - > get_props ( ) - > getNode ( " nasal " ) ;
if ( ! nasal ) return ;
2011-04-03 15:30:25 +02:00
for ( int i = 0 ; i < nasal - > nChildren ( ) ; i + + )
{
2003-12-01 14:35:49 +00:00
SGPropertyNode * n = nasal - > getChild ( i ) ;
2011-04-03 15:30:25 +02:00
loadPropertyScripts ( n ) ;
}
}
2003-12-01 14:35:49 +00:00
2011-04-03 15:30:25 +02:00
// Loads the scripts found under /nasal in the global tree
void FGNasalSys : : loadPropertyScripts ( SGPropertyNode * n )
{
bool is_loaded = false ;
const char * module = n - > getName ( ) ;
if ( n - > hasChild ( " module " ) )
module = n - > getStringValue ( " module " ) ;
if ( n - > getBoolValue ( " enabled " , true ) )
{
// allow multiple files to be specified within a single
// Nasal module tag
int j = 0 ;
SGPropertyNode * fn ;
bool file_specified = false ;
bool ok = true ;
while ( ( fn = n - > getChild ( " file " , j ) ) ! = NULL ) {
file_specified = true ;
const char * file = fn - > getStringValue ( ) ;
SGPath p ( file ) ;
if ( ! p . isAbsolute ( ) | | ! p . exists ( ) )
2011-04-02 15:13:29 +02:00
{
2011-04-03 15:30:25 +02:00
p = globals - > resolve_maybe_aircraft_path ( file ) ;
2011-07-03 13:06:41 +02:00
if ( p . isNull ( ) )
{
SG_LOG ( SG_NASAL , SG_ALERT , " Cannot find Nasal script ' " < <
file < < " ' for module ' " < < module < < " '. " ) ;
}
2011-04-02 15:13:29 +02:00
}
2011-07-03 13:06:41 +02:00
ok & = p . isNull ( ) ? false : loadModule ( p , module ) ;
2011-04-03 15:30:25 +02:00
j + + ;
}
const char * src = n - > getStringValue ( " script " ) ;
if ( ! n - > hasChild ( " script " ) ) src = 0 ; // Hrm...
if ( src )
createModule ( module , n - > getPath ( ) . c_str ( ) , src , strlen ( src ) ) ;
if ( ! file_specified & & ! src )
{
// module no longer exists - clear the archived "enable" flag
n - > setAttribute ( SGPropertyNode : : USERARCHIVE , false ) ;
SGPropertyNode * node = n - > getChild ( " enabled " , 0 , false ) ;
if ( node )
node - > setAttribute ( SGPropertyNode : : USERARCHIVE , false ) ;
SG_LOG ( SG_NASAL , SG_ALERT , " Nasal error: " < <
" no <file> or <script> defined in " < <
" /nasal/ " < < module ) ;
2005-03-16 21:36:55 +00:00
}
2011-04-03 15:30:25 +02:00
else
is_loaded = ok ;
2003-12-01 14:35:49 +00:00
}
2011-04-03 15:30:25 +02:00
else
{
SGPropertyNode * enable = n - > getChild ( " enabled " ) ;
if ( enable )
{
FGNasalModuleListener * listener = new FGNasalModuleListener ( n ) ;
2013-12-03 21:38:24 +00:00
_moduleListeners . push_back ( listener ) ;
2011-04-03 15:30:25 +02:00
enable - > addChangeListener ( listener , false ) ;
}
}
2011-06-12 20:31:56 +02:00
SGPropertyNode * loaded = n - > getChild ( " loaded " , 0 , true ) ;
loaded - > setAttribute ( SGPropertyNode : : PRESERVE , true ) ;
loaded - > setBoolValue ( is_loaded ) ;
2003-11-25 21:08:36 +00:00
}
// Logs a runtime error, with stack trace, to the FlightGear log stream
2006-01-27 19:51:25 +00:00
void FGNasalSys : : logError ( naContext context )
2003-11-25 21:08:36 +00:00
{
2013-10-16 23:36:27 +02:00
SG_LOG ( SG_NASAL , SG_ALERT , " Nasal runtime error: " < < naGetError ( context ) ) ;
int stack_depth = naStackDepth ( context ) ;
if ( stack_depth < 1 )
return ;
2003-11-25 21:08:36 +00:00
SG_LOG ( SG_NASAL , SG_ALERT ,
2006-01-27 19:51:25 +00:00
" at " < < naStr_data ( naGetSourceFile ( context , 0 ) ) < <
" , line " < < naGetLine ( context , 0 ) ) ;
2013-10-16 23:36:27 +02:00
for ( int i = 1 ; i < stack_depth ; i + + )
2003-11-25 21:08:36 +00:00
SG_LOG ( SG_NASAL , SG_ALERT ,
2006-01-27 19:51:25 +00:00
" called from: " < < naStr_data ( naGetSourceFile ( context , i ) ) < <
" , line " < < naGetLine ( context , i ) ) ;
2003-11-25 21:08:36 +00:00
}
// Reads a script file, executes it, and places the resulting
// namespace into the global namespace under the specified module
// name.
2011-04-03 15:30:25 +02:00
bool FGNasalSys : : loadModule ( SGPath file , const char * module )
2003-11-25 21:08:36 +00:00
{
int len = 0 ;
char * buf = readfile ( file . c_str ( ) , & len ) ;
2003-12-01 14:35:49 +00:00
if ( ! buf ) {
SG_LOG ( SG_NASAL , SG_ALERT ,
" Nasal error: could not read script file " < < file . c_str ( )
< < " into module " < < module ) ;
2011-04-03 15:30:25 +02:00
return false ;
2003-12-01 14:35:49 +00:00
}
2003-11-25 21:08:36 +00:00
2011-04-03 15:30:25 +02:00
bool ok = createModule ( module , file . c_str ( ) , buf , len ) ;
2003-11-25 21:08:36 +00:00
delete [ ] buf ;
2011-04-03 15:30:25 +02:00
return ok ;
2003-12-01 14:35:49 +00:00
}
// Parse and run. Save the local variables namespace, as it will
2006-04-27 16:40:04 +00:00
// become a sub-object of globals. The optional "arg" argument can be
// used to pass an associated property node to the module, which can then
// be accessed via cmdarg(). (This is, for example, used by XML dialogs.)
2011-04-03 15:30:25 +02:00
bool FGNasalSys : : createModule ( const char * moduleName , const char * fileName ,
2008-10-18 19:52:18 +00:00
const char * src , int len ,
const SGPropertyNode * cmdarg ,
int argc , naRef * args )
2003-12-01 14:35:49 +00:00
{
2014-04-15 14:13:46 +01:00
naContext ctx = naNewContext ( ) ;
naRef code = parse ( ctx , fileName , src , len ) ;
if ( naIsNil ( code ) ) {
naFreeContext ( ctx ) ;
2011-04-03 15:30:25 +02:00
return false ;
2014-04-15 14:13:46 +01:00
}
2003-11-25 21:08:36 +00:00
2013-11-22 22:40:50 +00:00
2003-12-01 14:35:49 +00:00
// See if we already have a module hash to use. This allows the
// user to, for example, add functions to the built-in math
// module. Make a new one if necessary.
naRef locals ;
2013-11-22 22:40:50 +00:00
naRef modname = naNewString ( ctx ) ;
2003-12-01 14:35:49 +00:00
naStr_fromdata ( modname , ( char * ) moduleName , strlen ( moduleName ) ) ;
if ( ! naHash_get ( _globals , modname , & locals ) )
2013-11-22 22:40:50 +00:00
locals = naNewHash ( ctx ) ;
2003-12-01 14:35:49 +00:00
2008-10-18 19:52:18 +00:00
_cmdArg = ( SGPropertyNode * ) cmdarg ;
2006-04-27 15:56:51 +00:00
2014-04-15 14:13:46 +01:00
callWithContext ( ctx , code , argc , args , locals ) ;
2003-12-01 14:35:49 +00:00
hashset ( _globals , moduleName , locals ) ;
2013-11-22 22:40:50 +00:00
naFreeContext ( ctx ) ;
2011-04-03 15:30:25 +02:00
return true ;
2003-11-25 21:08:36 +00:00
}
2006-03-08 10:35:20 +00:00
void FGNasalSys : : deleteModule ( const char * moduleName )
{
2014-03-16 22:52:55 +00:00
if ( ! _inited ) {
// can occur on shutdown due to us being shutdown first, but other
// subsystems having Nasal objects.
return ;
}
2013-11-22 22:40:50 +00:00
naContext ctx = naNewContext ( ) ;
naRef modname = naNewString ( ctx ) ;
2006-03-08 10:35:20 +00:00
naStr_fromdata ( modname , ( char * ) moduleName , strlen ( moduleName ) ) ;
naHash_delete ( _globals , modname ) ;
2013-11-22 22:40:50 +00:00
naFreeContext ( ctx ) ;
2006-03-08 10:35:20 +00:00
}
2014-04-15 14:13:46 +01:00
naRef FGNasalSys : : parse ( naContext ctx , const char * filename , const char * buf , int len )
2003-11-25 21:08:36 +00:00
{
int errLine = - 1 ;
2013-11-22 22:40:50 +00:00
naRef srcfile = naNewString ( ctx ) ;
2003-11-25 21:08:36 +00:00
naStr_fromdata ( srcfile , ( char * ) filename , strlen ( filename ) ) ;
2013-11-22 22:40:50 +00:00
naRef code = naParseCode ( ctx , srcfile , 1 , ( char * ) buf , len , & errLine ) ;
2003-11-25 21:08:36 +00:00
if ( naIsNil ( code ) ) {
SG_LOG ( SG_NASAL , SG_ALERT ,
2013-11-22 22:40:50 +00:00
" Nasal parse error: " < < naGetError ( ctx ) < <
2003-11-25 21:08:36 +00:00
" in " < < filename < < " , line " < < errLine ) ;
return naNil ( ) ;
}
// Bind to the global namespace before returning
2014-04-15 14:13:46 +01:00
return naBindFunction ( ctx , code , _globals ) ;
2003-11-25 21:08:36 +00:00
}
2012-07-04 13:15:12 +02:00
bool FGNasalSys : : handleCommand ( const char * moduleName ,
const char * fileName ,
const char * src ,
const SGPropertyNode * arg )
2003-11-25 21:08:36 +00:00
{
2014-04-15 14:13:46 +01:00
naContext ctx = naNewContext ( ) ;
naRef code = parse ( ctx , fileName , src , strlen ( src ) ) ;
if ( naIsNil ( code ) ) {
naFreeContext ( ctx ) ;
return false ;
}
2005-06-19 17:09:03 +00:00
2007-03-29 18:50:28 +00:00
// Commands can be run "in" a module. Make sure that module
// exists, and set it up as the local variables hash for the
// command.
2005-06-19 17:09:03 +00:00
naRef locals = naNil ( ) ;
2006-03-08 16:06:32 +00:00
if ( moduleName [ 0 ] ) {
2013-11-22 22:40:50 +00:00
naRef modname = naNewString ( ctx ) ;
2005-06-19 17:09:03 +00:00
naStr_fromdata ( modname , ( char * ) moduleName , strlen ( moduleName ) ) ;
2007-03-29 18:50:28 +00:00
if ( ! naHash_get ( _globals , modname , & locals ) ) {
2013-11-22 22:40:50 +00:00
locals = naNewHash ( ctx ) ;
2007-03-29 18:50:28 +00:00
naHash_set ( _globals , modname , locals ) ;
}
2005-06-19 17:09:03 +00:00
}
2007-03-29 18:50:28 +00:00
// Cache this command's argument for inspection via cmdarg(). For
2003-12-01 14:35:49 +00:00
// performance reasons, we won't bother with it if the invoked
// code doesn't need it.
_cmdArg = ( SGPropertyNode * ) arg ;
2003-11-25 21:08:36 +00:00
2014-04-15 14:13:46 +01:00
callWithContext ( ctx , code , 0 , 0 , locals ) ;
naFreeContext ( ctx ) ;
2006-07-19 19:46:53 +00:00
return true ;
2003-11-25 21:08:36 +00:00
}
2012-07-04 13:15:12 +02:00
bool FGNasalSys : : handleCommand ( const SGPropertyNode * arg )
{
const char * src = arg - > getStringValue ( " script " ) ;
const char * moduleName = arg - > getStringValue ( " module " ) ;
return handleCommand ( moduleName ,
2014-03-12 22:42:51 +01:00
arg - > getPath ( true ) . c_str ( ) ,
2012-07-04 13:15:12 +02:00
src ,
arg ) ;
}
2003-11-25 21:08:36 +00:00
// settimer(func, dt, simtime) extension function. The first argument
// is a Nasal function to call, the second is a delta time (from now),
// in seconds. The third, if present, is a boolean value indicating
2005-11-09 20:34:46 +00:00
// that "real world" time (rather than simulator time) is to be used.
2003-11-25 21:08:36 +00:00
//
// Implementation note: the FGTimer objects don't live inside the
// garbage collector, so the Nasal handler functions have to be
// "saved" somehow lest they be inadvertently cleaned. In this case,
2003-12-05 01:54:39 +00:00
// they are inserted into a globals.__gcsave hash and removed on
2003-11-25 21:08:36 +00:00
// expiration.
2007-05-12 18:15:45 +00:00
void FGNasalSys : : setTimer ( naContext c , int argc , naRef * args )
2003-11-25 21:08:36 +00:00
{
// Extract the handler, delta, and simtime arguments:
2005-04-18 19:49:13 +00:00
naRef handler = argc > 0 ? args [ 0 ] : naNil ( ) ;
2007-05-12 18:15:45 +00:00
if ( ! ( naIsCode ( handler ) | | naIsCCode ( handler ) | | naIsFunc ( handler ) ) ) {
naRuntimeError ( c , " settimer() with invalid function argument " ) ;
2003-11-25 21:08:36 +00:00
return ;
2007-05-12 18:15:45 +00:00
}
2003-11-25 21:08:36 +00:00
2005-04-18 19:49:13 +00:00
naRef delta = argc > 1 ? args [ 1 ] : naNil ( ) ;
2007-05-12 18:15:45 +00:00
if ( naIsNil ( delta ) ) {
naRuntimeError ( c , " settimer() with invalid time argument " ) ;
return ;
}
2006-02-28 14:55:37 +00:00
2005-11-09 20:34:46 +00:00
bool simtime = ( argc > 2 & & naTrue ( args [ 2 ] ) ) ? false : true ;
2003-11-25 21:08:36 +00:00
// Generate and register a C++ timer handler
NasalTimer * t = new NasalTimer ;
t - > handler = handler ;
2003-12-05 01:54:39 +00:00
t - > gcKey = gcSave ( handler ) ;
2003-11-25 21:08:36 +00:00
t - > nasal = this ;
globals - > get_event_mgr ( ) - > addEvent ( " NasalTimer " ,
t , & NasalTimer : : timerExpired ,
delta . num , simtime ) ;
}
void FGNasalSys : : handleTimer ( NasalTimer * t )
{
2007-10-15 16:28:40 +00:00
call ( t - > handler , 0 , 0 , naNil ( ) ) ;
2003-12-05 01:54:39 +00:00
gcRelease ( t - > gcKey ) ;
}
int FGNasalSys : : gcSave ( naRef r )
{
2013-10-15 00:58:04 +02:00
return naGCSave ( r ) ;
2003-12-05 01:54:39 +00:00
}
void FGNasalSys : : gcRelease ( int key )
{
2013-10-15 00:58:04 +02:00
naGCRelease ( key ) ;
2003-11-25 21:08:36 +00:00
}
2014-09-19 18:21:42 +02:00
//------------------------------------------------------------------------------
2003-11-25 21:08:36 +00:00
void FGNasalSys : : NasalTimer : : timerExpired ( )
{
nasal - > handleTimer ( this ) ;
delete this ;
}
2005-12-16 19:11:03 +00:00
2006-02-28 14:55:37 +00:00
int FGNasalSys : : _listenerId = 0 ;
2007-10-14 18:01:26 +00:00
// setlistener(<property>, <func> [, <initial=0> [, <persistent=1>]])
// Attaches a callback function to a property (specified as a global
2014-11-23 14:53:54 +01:00
// property path string or a SGPropertyNode* ghost). If the third,
2007-10-14 18:01:26 +00:00
// optional argument (default=0) is set to 1, then the function is also
// called initially. If the fourth, optional argument is set to 0, then the
// function is only called when the property node value actually changes.
// Otherwise it's called independent of the value whenever the node is
// written to (default). The setlistener() function returns a unique
// id number, which is to be used as argument to the removelistener()
// function.
2007-02-03 16:46:39 +00:00
naRef FGNasalSys : : setListener ( naContext c , int argc , naRef * args )
2005-12-16 19:11:03 +00:00
{
2006-02-28 14:55:37 +00:00
SGPropertyNode_ptr node ;
2005-12-16 19:11:03 +00:00
naRef prop = argc > 0 ? args [ 0 ] : naNil ( ) ;
if ( naIsString ( prop ) ) node = fgGetNode ( naStr_data ( prop ) , true ) ;
2014-11-23 14:53:54 +01:00
else if ( naIsGhost ( prop ) ) node = static_cast < SGPropertyNode * > ( naGhost_ptr ( prop ) ) ;
2007-02-03 16:46:39 +00:00
else {
naRuntimeError ( c , " setlistener() with invalid property argument " ) ;
return naNil ( ) ;
}
2005-12-16 19:11:03 +00:00
2007-01-23 15:53:04 +00:00
if ( node - > isTied ( ) )
2006-07-16 11:48:22 +00:00
SG_LOG ( SG_NASAL , SG_DEBUG , " Attaching listener to tied property " < <
2006-07-16 11:30:33 +00:00
node - > getPath ( ) ) ;
2007-10-18 11:43:38 +00:00
naRef code = argc > 1 ? args [ 1 ] : naNil ( ) ;
if ( ! ( naIsCode ( code ) | | naIsCCode ( code ) | | naIsFunc ( code ) ) ) {
2007-02-03 16:46:39 +00:00
naRuntimeError ( c , " setlistener() with invalid function argument " ) ;
2006-02-28 14:55:37 +00:00
return naNil ( ) ;
2007-02-03 16:46:39 +00:00
}
2005-12-16 19:11:03 +00:00
2009-03-07 17:57:20 +00:00
int init = argc > 2 & & naIsNum ( args [ 2 ] ) ? int ( args [ 2 ] . num ) : 0 ;
int type = argc > 3 & & naIsNum ( args [ 3 ] ) ? int ( args [ 3 ] . num ) : 1 ;
2007-10-18 11:43:38 +00:00
FGNasalListener * nl = new FGNasalListener ( node , code , this ,
2009-03-07 17:57:20 +00:00
gcSave ( code ) , _listenerId , init , type ) ;
2007-10-14 18:01:26 +00:00
2011-07-22 12:37:41 +02:00
node - > addChangeListener ( nl , init ! = 0 ) ;
2006-03-02 10:41:48 +00:00
_listener [ _listenerId ] = nl ;
2006-02-28 14:55:37 +00:00
return naNum ( _listenerId + + ) ;
}
// removelistener(int) extension function. The argument is the id of
// a listener as returned by the setlistener() function.
2007-01-17 13:56:22 +00:00
naRef FGNasalSys : : removeListener ( naContext c , int argc , naRef * args )
2006-02-28 14:55:37 +00:00
{
naRef id = argc > 0 ? args [ 0 ] : naNil ( ) ;
2007-01-23 16:47:04 +00:00
map < int , FGNasalListener * > : : iterator it = _listener . find ( int ( id . num ) ) ;
2007-01-17 13:56:22 +00:00
2007-01-23 16:47:04 +00:00
if ( ! naIsNum ( id ) | | it = = _listener . end ( ) | | it - > second - > _dead ) {
2007-01-17 13:56:22 +00:00
naRuntimeError ( c , " removelistener() with invalid listener id " ) ;
2006-02-28 14:55:37 +00:00
return naNil ( ) ;
2007-01-17 13:56:22 +00:00
}
2006-02-28 14:55:37 +00:00
2007-10-18 11:43:38 +00:00
it - > second - > _dead = true ;
_dead_listener . push_back ( it - > second ) ;
2007-01-23 16:47:04 +00:00
_listener . erase ( it ) ;
2006-03-02 10:41:48 +00:00
return naNum ( _listener . size ( ) ) ;
2005-12-16 19:11:03 +00:00
}
2013-02-09 15:33:05 +00:00
void FGNasalSys : : registerToLoad ( FGNasalModelData * data )
{
2013-03-23 12:53:17 +01:00
if ( _loadList . empty ( ) )
_delay_load = true ;
_loadList . push ( data ) ;
2013-02-09 15:33:05 +00:00
}
void FGNasalSys : : registerToUnload ( FGNasalModelData * data )
{
_unloadList . push ( data ) ;
}
2006-03-09 09:04:03 +00:00
2013-10-06 17:36:19 +01:00
void FGNasalSys : : addCommand ( naRef func , const std : : string & name )
{
if ( _commands . find ( name ) ! = _commands . end ( ) ) {
SG_LOG ( SG_NASAL , SG_WARN , " duplicate add of command: " < < name ) ;
return ;
}
NasalCommand * cmd = new NasalCommand ( this , func , name ) ;
_commands [ name ] = cmd ;
}
void FGNasalSys : : removeCommand ( const std : : string & name )
{
NasalCommandDict : : iterator it = _commands . find ( name ) ;
if ( it = = _commands . end ( ) ) {
SG_LOG ( SG_NASAL , SG_WARN , " remove of unknwon command: " < < name ) ;
return ;
}
// will delete the NasalCommand instance
globals - > get_commands ( ) - > removeCommand ( name ) ;
_commands . erase ( it ) ;
}
2013-03-16 12:44:27 +00:00
//////////////////////////////////////////////////////////////////////////
2006-03-20 07:13:10 +00:00
// FGNasalListener class.
2007-10-18 11:43:38 +00:00
FGNasalListener : : FGNasalListener ( SGPropertyNode * node , naRef code ,
2009-03-07 17:57:20 +00:00
FGNasalSys * nasal , int key , int id ,
int init , int type ) :
2006-03-20 07:13:10 +00:00
_node ( node ) ,
2007-10-18 11:43:38 +00:00
_code ( code ) ,
2006-03-20 07:13:10 +00:00
_gcKey ( key ) ,
2007-04-27 14:30:05 +00:00
_id ( id ) ,
2006-03-20 07:13:10 +00:00
_nas ( nasal ) ,
2009-03-07 17:57:20 +00:00
_init ( init ) ,
2007-10-15 16:28:40 +00:00
_type ( type ) ,
2007-01-23 16:47:04 +00:00
_active ( 0 ) ,
2007-10-12 17:24:43 +00:00
_dead ( false ) ,
_last_int ( 0L ) ,
_last_float ( 0.0 )
2006-03-20 07:13:10 +00:00
{
2009-03-07 17:57:20 +00:00
if ( _type = = 0 & & ! _init )
changed ( node ) ;
2006-03-20 07:13:10 +00:00
}
FGNasalListener : : ~ FGNasalListener ( )
{
2006-06-10 22:21:22 +00:00
_node - > removeChangeListener ( this ) ;
2006-03-20 07:13:10 +00:00
_nas - > gcRelease ( _gcKey ) ;
}
2007-10-16 15:15:41 +00:00
void FGNasalListener : : call ( SGPropertyNode * which , naRef mode )
2006-03-20 07:13:10 +00:00
{
2007-10-16 15:15:41 +00:00
if ( _active | | _dead ) return ;
2007-10-15 16:28:40 +00:00
_active + + ;
2007-10-16 15:15:41 +00:00
naRef arg [ 4 ] ;
arg [ 0 ] = _nas - > propNodeGhost ( which ) ;
arg [ 1 ] = _nas - > propNodeGhost ( _node ) ;
arg [ 2 ] = mode ; // value changed, child added/removed
arg [ 3 ] = naNum ( _node ! = which ) ; // child event?
2007-10-18 11:43:38 +00:00
_nas - > call ( _code , 4 , arg , naNil ( ) ) ;
2007-10-15 16:28:40 +00:00
_active - - ;
}
void FGNasalListener : : valueChanged ( SGPropertyNode * node )
{
2007-10-18 11:43:38 +00:00
if ( _type < 2 & & node ! = _node ) return ; // skip child events
2009-03-07 17:57:20 +00:00
if ( _type > 0 | | changed ( _node ) | | _init )
2007-10-16 15:15:41 +00:00
call ( node , naNum ( 0 ) ) ;
2007-10-15 16:28:40 +00:00
2009-03-07 17:57:20 +00:00
_init = 0 ;
2006-03-20 07:13:10 +00:00
}
2007-10-15 19:12:03 +00:00
void FGNasalListener : : childAdded ( SGPropertyNode * , SGPropertyNode * child )
2007-10-15 16:28:40 +00:00
{
2007-10-16 15:15:41 +00:00
if ( _type = = 2 ) call ( child , naNum ( 1 ) ) ;
2007-10-15 16:28:40 +00:00
}
2007-10-15 19:12:03 +00:00
void FGNasalListener : : childRemoved ( SGPropertyNode * , SGPropertyNode * child )
2007-10-15 16:28:40 +00:00
{
2007-10-16 15:15:41 +00:00
if ( _type = = 2 ) call ( child , naNum ( - 1 ) ) ;
2007-10-15 16:28:40 +00:00
}
2007-10-14 18:01:26 +00:00
bool FGNasalListener : : changed ( SGPropertyNode * node )
2007-10-12 17:24:43 +00:00
{
2009-07-17 14:54:12 +02:00
using namespace simgear ;
props : : Type type = node - > getType ( ) ;
if ( type = = props : : NONE ) return false ;
if ( type = = props : : UNSPECIFIED ) return true ;
2007-10-12 17:24:43 +00:00
bool result ;
switch ( type ) {
2009-07-17 14:54:12 +02:00
case props : : BOOL :
case props : : INT :
case props : : LONG :
2007-10-12 17:24:43 +00:00
{
long l = node - > getLongValue ( ) ;
2007-10-14 18:01:26 +00:00
result = l ! = _last_int ;
2007-10-12 17:24:43 +00:00
_last_int = l ;
return result ;
}
2009-07-17 14:54:12 +02:00
case props : : FLOAT :
case props : : DOUBLE :
2007-10-12 17:24:43 +00:00
{
double d = node - > getDoubleValue ( ) ;
2007-10-14 18:01:26 +00:00
result = d ! = _last_float ;
2007-10-12 17:24:43 +00:00
_last_float = d ;
return result ;
}
default :
{
string s = node - > getStringValue ( ) ;
2007-10-14 18:01:26 +00:00
result = s ! = _last_string ;
2007-10-12 17:24:43 +00:00
_last_string = s ;
return result ;
}
}
}
2006-03-20 07:13:10 +00:00
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
// NasalXMLVisitor class: handles EasyXML visitor callback for parsexml()
//
NasalXMLVisitor : : NasalXMLVisitor ( naContext c , int argc , naRef * args ) :
_c ( naSubContext ( c ) ) ,
2007-07-01 11:07:53 +00:00
_start_element ( argc > 1 ? args [ 1 ] : naNil ( ) ) ,
_end_element ( argc > 2 ? args [ 2 ] : naNil ( ) ) ,
_data ( argc > 3 ? args [ 3 ] : naNil ( ) ) ,
_pi ( argc > 4 ? args [ 4 ] : naNil ( ) )
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
{
}
void NasalXMLVisitor : : startElement ( const char * tag , const XMLAttributes & a )
{
if ( naIsNil ( _start_element ) ) return ;
naRef attr = naNewHash ( _c ) ;
for ( int i = 0 ; i < a . size ( ) ; i + + ) {
naRef name = make_string ( a . getName ( i ) ) ;
naRef value = make_string ( a . getValue ( i ) ) ;
naHash_set ( attr , name , value ) ;
}
call ( _start_element , 2 , make_string ( tag ) , attr ) ;
}
void NasalXMLVisitor : : endElement ( const char * tag )
{
if ( ! naIsNil ( _end_element ) ) call ( _end_element , 1 , make_string ( tag ) ) ;
}
void NasalXMLVisitor : : data ( const char * str , int len )
{
if ( ! naIsNil ( _data ) ) call ( _data , 1 , make_string ( str , len ) ) ;
}
void NasalXMLVisitor : : pi ( const char * target , const char * data )
{
2007-09-30 11:56:21 +00:00
if ( ! naIsNil ( _pi ) ) call ( _pi , 2 , make_string ( target ) , make_string ( data ) ) ;
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
}
void NasalXMLVisitor : : call ( naRef func , int num , naRef a , naRef b )
{
2007-06-30 09:44:33 +00:00
naRef args [ 2 ] ;
args [ 0 ] = a ;
args [ 1 ] = b ;
naCall ( _c , func , num , args , naNil ( ) , naNil ( ) ) ;
add parsexml() function, which is a wrapper around the built-in easyxml
parser. Advantages over xml.nas: (reviewed and OK'ed by Andy)
- faster (33% ... only. I had hoped for more.)
- more standards compliant
- should support UTF
- I don't have to support it. ;-)
Usage: parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
<path> is an absolute file path, the rest are optional callback functions.
Example:
parsexml("/tmp/foo.xml", nil, nil, func(d) { print("DATA FOUND: ", d) });
2007-06-29 15:34:38 +00:00
if ( naGetError ( _c ) )
naRethrowError ( _c ) ;
}
naRef NasalXMLVisitor : : make_string ( const char * s , int n )
{
return naStr_fromdata ( naNewString ( _c ) , const_cast < char * > ( s ) ,
n < 0 ? strlen ( s ) : n ) ;
}